Update vendor
This commit is contained in:
parent
7a2ae81363
commit
77df37ca67
14
glide.lock
generated
14
glide.lock
generated
@ -1,5 +1,5 @@
|
||||
hash: b5b9ebebad30becd361736196a015af23b1d9a616a375c7fc13823121fd17226
|
||||
updated: 2017-06-05T16:11:29.019891941-04:00
|
||||
hash: d9cd9bf3ab1048a80f5ad90e05a7ea4c7614c30c561199b71147609de600a524
|
||||
updated: 2018-01-17T13:29:36.176987+01:00
|
||||
imports:
|
||||
- name: github.com/beorn7/perks
|
||||
version: 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
|
||||
@ -23,9 +23,19 @@ imports:
|
||||
- jsonpb
|
||||
- proto
|
||||
- protoc-gen-go/descriptor
|
||||
- ptypes
|
||||
- ptypes/any
|
||||
- ptypes/duration
|
||||
- ptypes/empty
|
||||
- ptypes/struct
|
||||
- ptypes/timestamp
|
||||
- name: github.com/grafeas/grafeas
|
||||
version: 73210e9cadcba64b5b211a0ec64a9f4c2d4841b5
|
||||
repo: https://github.com/Grafeas/Grafeas.git
|
||||
vcs: git
|
||||
subpackages:
|
||||
- samples/server/go-server/api/server/name
|
||||
- v1alpha1/proto
|
||||
- name: github.com/grpc-ecosystem/go-grpc-prometheus
|
||||
version: 2500245aa6110c562d17020fb31a2c133d737799
|
||||
- name: github.com/grpc-ecosystem/grpc-gateway
|
||||
|
@ -30,3 +30,6 @@ import:
|
||||
version: ^1.2.15
|
||||
- package: gopkg.in/yaml.v2
|
||||
- package: github.com/cockroachdb/cmux
|
||||
- package: github.com/grafeas/grafeas
|
||||
vcs: git
|
||||
repo: https://github.com/Grafeas/Grafeas.git
|
||||
|
48
vendor/github.com/grafeas/grafeas/.circleci/config.yml
generated
vendored
Normal file
48
vendor/github.com/grafeas/grafeas/.circleci/config.yml
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
# Golang CircleCI 2.0 configuration file
|
||||
#
|
||||
# Check https://circleci.com/docs/2.0/language-go/ for more details
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
# specify the version
|
||||
- image: circleci/golang:1.8
|
||||
# Specify service dependencies here if necessary
|
||||
# CircleCI maintains a library of pre-built images
|
||||
# documented at https://circleci.com/docs/2.0/circleci-images/
|
||||
# - image: circleci/postgres:9.4
|
||||
|
||||
#### TEMPLATE_NOTE: go expects specific checkout path representing url
|
||||
#### expecting it in the form of
|
||||
#### /go/src/github.com/circleci/go-tool
|
||||
#### /go/src/bitbucket.org/circleci/go-tool
|
||||
working_directory: /go/src/github.com/grafeas/grafeas/
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install protoc
|
||||
command: >
|
||||
sudo apt-get update
|
||||
|
||||
sudo wget
|
||||
https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-linux-x86_64.zip
|
||||
|
||||
unzip protoc-3.3.0-linux-x86_64.zip -d .
|
||||
|
||||
rm protoc-3.3.0-linux-x86_64.zip
|
||||
|
||||
sudo apt-get remove -y wget unzip
|
||||
|
||||
sudo apt-get autoremove -y
|
||||
|
||||
- run:
|
||||
name: Make protoc executables available globally
|
||||
command: |
|
||||
echo 'export PATH=./bin:$PATH' >> $BASH_ENV
|
||||
source /home/circleci/.bashrc
|
||||
|
||||
- run:
|
||||
name: Install go dependencies
|
||||
command: go get -u -v github.com/golang/protobuf/protoc-gen-go github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
|
||||
# specify any bash command here prefixed with `run: `
|
||||
- run: make build; make test
|
1
vendor/github.com/grafeas/grafeas/.gitignore
generated
vendored
Normal file
1
vendor/github.com/grafeas/grafeas/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
.install.protoc-gen-go
|
9
vendor/github.com/grafeas/grafeas/.idea/go.imports.xml
generated
vendored
Normal file
9
vendor/github.com/grafeas/grafeas/.idea/go.imports.xml
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GoImports">
|
||||
<option name="groupStdlibImports" value="true" />
|
||||
<option name="importSorting" value="GOFMT" />
|
||||
<option name="moveAllImportsInOneDeclaration" value="true" />
|
||||
<option name="moveAllStdlibImportsInOneGroup" value="true" />
|
||||
</component>
|
||||
</project>
|
7
vendor/github.com/grafeas/grafeas/AUTHORS
generated
vendored
Normal file
7
vendor/github.com/grafeas/grafeas/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# This is the list of Grafeas authors for copyright purposes.
|
||||
#
|
||||
# This does not necessarily list everyone who has contributed code, since in
|
||||
# some cases, their employer may be the copyright holder. To see the full list
|
||||
# of contributors, see the revision history in source control.
|
||||
Google Inc.
|
||||
JFrog Ltd
|
30
vendor/github.com/grafeas/grafeas/CONTRIBUTING.md
generated
vendored
Normal file
30
vendor/github.com/grafeas/grafeas/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# How to Contribute
|
||||
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
just a few small guidelines you need to follow.
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
Contributions to this project must be accompanied by a Contributor License
|
||||
Agreement. You (or your employer) retain the copyright to your contribution,
|
||||
this simply gives us permission to use and redistribute your contributions as
|
||||
part of the project. Head over to <https://cla.developers.google.com/> to see
|
||||
your current agreements on file or to sign a new one.
|
||||
|
||||
You generally only need to submit a CLA once, so if you've already submitted one
|
||||
(even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
## Proposals and PRs
|
||||
|
||||
If you would like to make a large change, please start with a proposal issue that includes:
|
||||
* What you would like to achieve
|
||||
* Why you'd like to make this change
|
||||
* A design overview
|
||||
|
||||
## Code reviews
|
||||
|
||||
All submissions, including submissions by project members, require review. We
|
||||
use GitHub pull requests for this purpose. Consult
|
||||
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
|
||||
information on using pull requests.
|
10
vendor/github.com/grafeas/grafeas/Dockerfile
generated
vendored
Normal file
10
vendor/github.com/grafeas/grafeas/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
FROM golang:1.9
|
||||
COPY . /go/src/github.com/grafeas/grafeas/
|
||||
WORKDIR /go/src/github.com/grafeas/grafeas/samples/server/go-server/api/server/main
|
||||
RUN CGO_ENABLED=0 go build -o grafeas-server .
|
||||
|
||||
FROM alpine:latest
|
||||
WORKDIR /
|
||||
COPY --from=0 /go/src/github.com/grafeas/grafeas/samples/server/go-server/api/server/main/grafeas-server /grafeas-server
|
||||
EXPOSE 8080
|
||||
CMD ["/grafeas-server"]
|
201
vendor/github.com/grafeas/grafeas/LICENSE
generated
vendored
Normal file
201
vendor/github.com/grafeas/grafeas/LICENSE
generated
vendored
Normal file
@ -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 2017 The Grafeas Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
46
vendor/github.com/grafeas/grafeas/Makefile
generated
vendored
Normal file
46
vendor/github.com/grafeas/grafeas/Makefile
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
.PHONY: build fmt test vet clean
|
||||
|
||||
SRC = $(shell find . -type f -name '*.go' -not -path "./vendor/*")
|
||||
CLEAN := *~
|
||||
|
||||
default: build
|
||||
|
||||
install.tools: .install.protoc-gen-go .install.grpc-gateway
|
||||
|
||||
CLEAN += .install.protoc-gen-go .install.grpc-gateway
|
||||
.install.protoc-gen-go:
|
||||
go get -u -v github.com/golang/protobuf/protoc-gen-go && touch $@
|
||||
|
||||
.install.grpc-gateway:
|
||||
go get -u -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger && touch $@
|
||||
|
||||
build: vet fmt grafeas_go
|
||||
go build -v ./...
|
||||
|
||||
# http://golang.org/cmd/go/#hdr-Run_gofmt_on_package_sources
|
||||
fmt:
|
||||
@gofmt -l -w $(SRC)
|
||||
|
||||
test:
|
||||
@go test -v ./...
|
||||
|
||||
vet:
|
||||
@go tool vet ${SRC}
|
||||
|
||||
v1alpha1/proto/grafeas.pb.go: .install.protoc-gen-go .install.grpc-gateway v1alpha1/proto/grafeas.proto
|
||||
protoc \
|
||||
-I ./ \
|
||||
-I vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
|
||||
-I vendor/github.com/googleapis/googleapis \
|
||||
--go_out=plugins=grpc:. \
|
||||
--grpc-gateway_out=logtostderr=true:. \
|
||||
--swagger_out=logtostderr=true:. \
|
||||
v1alpha1/proto/grafeas.proto
|
||||
|
||||
|
||||
.PHONY: grafeas_go
|
||||
grafeas_go: v1alpha1/proto/grafeas.pb.go
|
||||
|
||||
clean:
|
||||
go clean ./...
|
||||
rm -f $(CLEAN)
|
216
vendor/github.com/grafeas/grafeas/README.md
generated
vendored
Normal file
216
vendor/github.com/grafeas/grafeas/README.md
generated
vendored
Normal file
@ -0,0 +1,216 @@
|
||||
# Grafeas: A Component Metadata API
|
||||
Grafeas defines metadata API spec for computing components (e.g., VM images, container images, jar files, scripts) that can assist with aggregations over your metadata. Grafeas uses two API concepts, a **note** and an **occurrence**. This division allows 3rd party metadata providers to create and manage metadata on behalf of many customers. Additionally, the division also allows implementation of access control settings that allow fine grain access control.
|
||||
|
||||
## Running grafeas
|
||||
|
||||
To run your own Grafeas instance just follow the [instructions](docs/running_greafeas.md).
|
||||
|
||||
## Definition of terms
|
||||
**Notes**: A note is an item or condition that can be found via an analysis or something that is used multiple times in a process. For example, a CVE could be the result of a vulnerability analysis of a Linux package. In a build process, we would store information about our builder in a note.
|
||||
|
||||
A note name should take the format `/projects/<project_id>/notes/<note_id>` where the project_id would typically be different from the project where the occurrence is created and the note_id would be unique per note-project, and informative if possible.
|
||||
|
||||
Access to notes should be read-only for users who have access to occurrences referencing them, and editable only by the note owner.
|
||||
|
||||
**Occurrences**: An occurrence can be thought of as an instantiation of a note and describes how the note was found in a specific cloud resource or project (e.g., location, specific remediation steps, etc.), or what the results of a specific note were (e.g., the container images that resulted from a build). For example, an occurrence might report that the heartbleed OpenSSL bug (a possible Note) was found in a specific package of a container image, and include information about how to remedy the heartbleed bug based on the customer’s package.
|
||||
|
||||
An occurrence name should take the format `/projects/<project_id>/occurrences/<occurrence_id>` where the project_id would typically be different from the project where the note is created and the occurrence_id would be unique per occurrence-project, and would often be random.
|
||||
|
||||
Write access to occurrences should only be granted to users who have access to link a note to the occurrence. Any users can have read access to occurrences.
|
||||
|
||||
## Kind Specific Schemas
|
||||
In order to properly aggregate over metadata stored in Grafeas, each kind of information stored has a strict schema. These schemas allow normalization of data from multiple providers, giving users the ability to see meaningful insights in their components over time. Defined below are the currently supported kinds, and a brief summary of what the notes and occurrences for each of them will contain.
|
||||
Specifying a kind in our notes and occurrences makes Grafeas extensible. As new metadata types need support, new kinds can be added, each with their own schema.
|
||||
|
||||
>TODO:Document the process for adding a new kind to the spec and generating the model, documents, and client libraries to include that kind. #38
|
||||
|
||||
|
||||
|Kind |Note Summary |Occurrence Summary |
|
||||
|---------------------|-------------------------------------------------------------------------|-------------------------------------------------|
|
||||
|PACKAGE_VULNERABILITY|CVE or vulnerability description and details including severity, versions|Affected packages/versions in a specific resource|
|
||||
|BUILD_DETAILS |Builder version and signature |Details of this specific build including inputs and outputs|
|
||||
|IMAGE_BASIS |Base Image for a container |An image that uses the base image, and layers included on top of base image|
|
||||
|PACKAGE_MANAGER |Package Descriptions |Filesystem locations of where the package is installed in a specific resource|
|
||||
|DEPLOYMENT_HISTORY |A resource that can be deployed |Details of each deployment of the resource|
|
||||
|ATTESTATION |A logical attestation "role" or "authority", used as an anchor for attestations|An attestation by an authority for a specific property and resource|
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
A vulnerability scanning provider would create a note under their project with the following json for CVE-2017-14159
|
||||
```
|
||||
{
|
||||
"name": "projects/security-scanner/notes/CVE-2017-14159",
|
||||
"shortDescription": "CVE-2017-14159",
|
||||
"longDescription": "NIST vectors: AV:L/AC:M/Au:N/C:N/I:N",
|
||||
"relatedUrl": [
|
||||
{
|
||||
"url": "https://security-tracker.debian.org/tracker/CVE-2017-14159",
|
||||
"label": "More Info"
|
||||
},
|
||||
{
|
||||
"url": "http://people.ubuntu.com/~ubuntu-security/cve/CVE-2017-14159",
|
||||
"label": "More Info"
|
||||
}
|
||||
],
|
||||
"kind": "PACKAGE_VULNERABILITY",
|
||||
"createTime": "2017-09-05T21:44:52.071982Z",
|
||||
"updateTime": "2017-09-29T16:16:01.140652Z",
|
||||
"vulnerabilityType": {
|
||||
"cvssScore": 1.9,
|
||||
"severity": "LOW",
|
||||
"details": [
|
||||
{
|
||||
"cpeUri": "cpe:/o:debian:debian_linux:7",
|
||||
"severityName": "LOW",
|
||||
"fixedLocation": {
|
||||
"cpeUri": "cpe:/o:debian:debian_linux:7",
|
||||
"package": "openldap",
|
||||
"version": {
|
||||
"kind": "MAXIMUM"
|
||||
}
|
||||
},
|
||||
"minAffectedVersion": {
|
||||
"kind": "MINIMUM"
|
||||
},
|
||||
"package": "openldap",
|
||||
"description": "slapd in OpenLDAP 2.4.45 and earlier creates a PID file after dropping privileges to a non-root account, which might allow local users to kill arbitrary processes by leveraging access to this non-root account for PID file modification before a root script executes a \"kill `cat /pathname`\" command, as demonstrated by openldap-initscript."
|
||||
},
|
||||
{
|
||||
"cpeUri": "cpe:/o:debian:debian_linux:unstable",
|
||||
"severityName": "LOW",
|
||||
"fixedLocation": {
|
||||
"cpeUri": "cpe:/o:debian:debian_linux:unstable",
|
||||
"package": "openldap",
|
||||
"version": {
|
||||
"kind": "MAXIMUM"
|
||||
}
|
||||
},
|
||||
"minAffectedVersion": {
|
||||
"kind": "MINIMUM"
|
||||
},
|
||||
"package": "openldap",
|
||||
"description": "slapd in OpenLDAP 2.4.45 and earlier creates a PID file after dropping privileges to a non-root account, which might allow local users to kill arbitrary processes by leveraging access to this non-root account for PID file modification before a root script executes a \"kill `cat /pathname`\" command, as demonstrated by openldap-initscript."
|
||||
},
|
||||
{
|
||||
"cpeUri": "cpe:/o:debian:debian_linux:9",
|
||||
"severityName": "LOW",
|
||||
"fixedLocation": {
|
||||
"cpeUri": "cpe:/o:debian:debian_linux:9",
|
||||
"package": "openldap",
|
||||
"version": {
|
||||
"kind": "MAXIMUM"
|
||||
}
|
||||
},
|
||||
"minAffectedVersion": {
|
||||
"kind": "MINIMUM"
|
||||
},
|
||||
"package": "openldap",
|
||||
"description": "slapd in OpenLDAP 2.4.45 and earlier creates a PID file after dropping privileges to a non-root account, which might allow local users to kill arbitrary processes by leveraging access to this non-root account for PID file modification before a root script executes a \"kill `cat /pathname`\" command, as demonstrated by openldap-initscript."
|
||||
},
|
||||
{
|
||||
"cpeUri": "cpe:/o:debian:debian_linux:8",
|
||||
"severityName": "LOW",
|
||||
"fixedLocation": {
|
||||
"cpeUri": "cpe:/o:debian:debian_linux:8",
|
||||
"package": "openldap",
|
||||
"version": {
|
||||
"kind": "MAXIMUM"
|
||||
}
|
||||
},
|
||||
"minAffectedVersion": {
|
||||
"kind": "MINIMUM"
|
||||
},
|
||||
"package": "openldap",
|
||||
"description": "slapd in OpenLDAP 2.4.45 and earlier creates a PID file after dropping privileges to a non-root account, which might allow local users to kill arbitrary processes by leveraging access to this non-root account for PID file modification before a root script executes a \"kill `cat /pathname`\" command, as demonstrated by openldap-initscript."
|
||||
},
|
||||
{
|
||||
"cpeUri": "cpe:/o:canonical:ubuntu_linux:14.04",
|
||||
"severityName": "LOW",
|
||||
"fixedLocation": {
|
||||
"cpeUri": "cpe:/o:canonical:ubuntu_linux:14.04",
|
||||
"package": "openldap",
|
||||
"version": {
|
||||
"kind": "MAXIMUM"
|
||||
}
|
||||
},
|
||||
"minAffectedVersion": {
|
||||
"kind": "MINIMUM"
|
||||
},
|
||||
"package": "openldap",
|
||||
"description": "slapd in OpenLDAP 2.4.45 and earlier creates a PID file after dropping privileges to a non-root account, which might allow local users to kill arbitrary processes by leveraging access to this non-root account for PID file modification before a root script executes a \"kill `cat /pathname`\" command, as demonstrated by openldap-initscript."
|
||||
},
|
||||
{
|
||||
"cpeUri": "cpe:/o:canonical:ubuntu_linux:16.04",
|
||||
"severityName": "LOW",
|
||||
"fixedLocation": {
|
||||
"cpeUri": "cpe:/o:canonical:ubuntu_linux:16.04",
|
||||
"package": "openldap",
|
||||
"version": {
|
||||
"kind": "MAXIMUM"
|
||||
}
|
||||
},
|
||||
"minAffectedVersion": {
|
||||
"kind": "MINIMUM"
|
||||
},
|
||||
"package": "openldap",
|
||||
"description": "slapd in OpenLDAP 2.4.45 and earlier creates a PID file after dropping privileges to a non-root account, which might allow local users to kill arbitrary processes by leveraging access to this non-root account for PID file modification before a root script executes a \"kill `cat /pathname`\" command, as demonstrated by openldap-initscript."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
On scanning and coming across this vulnerability, a security scanning provider would create the following in their customer’s project:
|
||||
|
||||
```
|
||||
{
|
||||
"name": "projects/scanning-customer/occurrences/randomId1234",
|
||||
"resourceUrl": "https://gcr.io/scanning-customer/dockerimage@sha256:hash",
|
||||
"noteName": "projects/security-scanner/notes/CVE-2017-14159",
|
||||
"kind": "PACKAGE_VULNERABILITY",
|
||||
"createTime": "2017-09-29T02:58:23.376798Z",
|
||||
"updateTime": "2017-09-29T07:35:22.141762Z",
|
||||
"vulnerabilityDetails": {
|
||||
"severity": "LOW",
|
||||
"cvssScore": 1.9,
|
||||
"packageIssue": [
|
||||
{
|
||||
"affectedLocation": {
|
||||
"cpeUri": "cpe:/o:debian:debian_linux:8",
|
||||
"package": "openldap",
|
||||
"version": {
|
||||
"name": "2.4.40+dfsg",
|
||||
"revision": "1+deb8u2"
|
||||
}
|
||||
},
|
||||
"fixedLocation": {
|
||||
"cpeUri": "cpe:/o:debian:debian_linux:8",
|
||||
"package": "openldap",
|
||||
"version": {
|
||||
"kind": "MAXIMUM"
|
||||
}
|
||||
},
|
||||
"severityName": "LOW"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Resource Urls
|
||||
Component resource Urls need to be unique per resource as well as immutable. This will mean that the metadata associated with a resourceUrl will always be associated with exactly one component, and what is pointed at should never change. Content addressable resource urls are preferred. In the case with resources that cannot be immutable, a timestamp should be appended.
|
||||
|
||||
The following table provides examples one could use as resource urls for several component types:
|
||||
|
||||
Component Type|Identifier |Example|
|
||||
--------------|--------------------------------------------|-------|
|
||||
|Debian |deb://dist(optional):arch:name:version |deb://lucid:i386:acl:2.2.49-2|
|
||||
|Docker |https://Namespace/name@sha256:<Checksum> |https://gcr.io/scanning-customer/dockerimage@sha256:244fd47e07d1004f0aed9c156aa09083c82bf8944eceb67c946ff7430510a77b|
|
||||
|Generic file |file://sha256:<Checksum>:name |file://sha256:244fd47e07d1004f0aed9c156aa09083c82bf8944eceb67c946ff7430510a77b:foo.jar|
|
||||
|Maven |gav://group:artifact:version |`gav://ant:ant:1.6.5`|
|
||||
|NPM |npm://package:version |npm://mocha:2.4.5|
|
||||
|NuGet |nuget://module:version |nuget://log4net:9.0.1|
|
||||
|Python |pip://package:version |pip://raven:5.13.0|
|
||||
|RPM |rpm://dist(optional):arch:name:version |rpm://el6:i386:ImageMagick:6.7.2.7-4|
|
240
vendor/github.com/grafeas/grafeas/case-studies/binary-authorization.md
generated
vendored
Normal file
240
vendor/github.com/grafeas/grafeas/case-studies/binary-authorization.md
generated
vendored
Normal file
@ -0,0 +1,240 @@
|
||||
# Kritis: Deployment Authorization for Kubernetes Applications
|
||||
|
||||
## Abstract
|
||||
|
||||
Binary Authorization aims to provide full software supply chain security for
|
||||
cloud based applications. In an initial release we enable customers to secure
|
||||
their supply chain for Kubernetes applications using an attestation based
|
||||
enforcement technology.
|
||||
|
||||
## Introduction
|
||||
|
||||
Organizations increasingly employ short software life cycle (for example
|
||||
continuous delivery) and highly decoupled systems (for example microservice
|
||||
architecture). In such environments it becomes difficult to ensure that all
|
||||
software is released and deployed according to best practices and standards.
|
||||
This is important because running, say, a wrong version of a binary, through
|
||||
accident or malice, may result in downtime, loss of user data, financial loss,
|
||||
or worse damage. Binary Authorization wants to address this major concern of
|
||||
today’s organizations: central control and enforcement of software life cycle
|
||||
process.
|
||||
|
||||
Binary Authorization allows stakeholders to ensure that deployed software
|
||||
artifacts have been prepared according to organization’s standards. It does so
|
||||
through attestation and enforcement: a deployment is prevented unless the
|
||||
artifact is conformant to central policy; and to express evidence of
|
||||
conformance, teams use trusted attestations. A Binary Authorization policy then
|
||||
states attestation requirements necessary for artifact deployment. Policy thus
|
||||
codifies an important part of organization’s life cycle policy.
|
||||
|
||||
### Scope
|
||||
|
||||
Kritis is our initiative to provide an open implementation of Binary
|
||||
Authorization. This paper discusses the general ideas behind Binary
|
||||
Authorization, and as such is a first part of the Kritis initiative. It further
|
||||
touches upon some specifics of our existing implementation of Binary
|
||||
Authorization, which in its first release is focused on Google Container Engine
|
||||
(GKE).
|
||||
|
||||
### Software Life Cycle Within an Organization
|
||||
|
||||
Each organization uses a release process tailored to specific needs and
|
||||
constraints. Binary Authorization does not prescribe any process, it instead
|
||||
helps codify and enforce the process that makes sense to the organization. To
|
||||
see how Binary Authorization fits into a release process, consider the following
|
||||
common pattern. Once a release is cut, artifacts go through the following
|
||||
stages, successful completion of a stage being the prerequisite for progression
|
||||
to the next one:
|
||||
|
||||
* Build and unit test.
|
||||
* Deploy into development environment, where users aren’t affected.
|
||||
* End to end testing might occur here.
|
||||
* Deploy into QA environment, where only internal users are affected.
|
||||
* Deploy into canary environment, where only a fraction of external users are
|
||||
affected.
|
||||
* Deploy into production.
|
||||
|
||||
When an artifact successfully completes a stage, an attestation on that artifact
|
||||
is created which asserts success. The policy requires previous stage’s
|
||||
attestations in order to allow deployment into next stage’s environment. In this
|
||||
way, Binary Authorization policy increases assurance that release process is
|
||||
followed. The policy may also specify that only recently attested artifacts be
|
||||
allowed deployment, and so ensure freshness.
|
||||
|
||||
#### Third Party Dependencies
|
||||
|
||||
Organizations rarely develop software from blank slate. Most of the time there
|
||||
is some reliance on third party “canned” software, e.g. sidecar container images
|
||||
from public repositories. Ideally, third party dependencies should be subject to
|
||||
the same scrutiny as internally developed software, however this is rarely
|
||||
practical. Compromises are usually made, for example by vetting third party
|
||||
container images as practically possible, and mandating the use of only tested
|
||||
and vetted versions of the software. Binary Authorization supports this use
|
||||
case.
|
||||
|
||||
### Example
|
||||
|
||||
An online merchant runs their services on Kubernetes. They have two clusters:
|
||||
`production` to run production approved services, and `experimental` to run
|
||||
experimental versions of services, but only 1% of the traffic is directed to the
|
||||
experimental cluster. Production versions of the software must pass a stringent
|
||||
test, while experimental versions, serving only a fraction of the traffic, are
|
||||
subject to less strict criteria. Both experimental and production software must
|
||||
pass a basic suite of tests. This organization wants to reduce the risk of
|
||||
accidentally deploying experimental software to the production cluster.
|
||||
Following policy realizes their requirements:
|
||||
|
||||
```
|
||||
{
|
||||
“cluster_admission_requirements”: {
|
||||
“cluster_name”: “prod”,
|
||||
“require_attestations”: [ “tested”, “production-approved” ]
|
||||
}
|
||||
“cluster_admission_requirements”: {
|
||||
“cluster_name”: “experimental”,
|
||||
“require_attestations”: [ “tested” ]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this organization, the release process has to be designed to respect this
|
||||
policy. Production qualification process must create the `production-approved`
|
||||
attestation on artifacts which have indeed passed the qualification. Continuous
|
||||
testing system must create the `tested` attestation.
|
||||
|
||||
## Binary Authorization Model in Google Container Engine (GKE)
|
||||
|
||||
Binary Authorization for GKE (BinAuthz) is available as an Alpha release. We
|
||||
overview its design, discussing some of the more interesting choices. In a
|
||||
nutshell, a user sets a BinAuthz policy for a GCP project; this policy specifies
|
||||
attestations required to deploy a container image into the project or into a
|
||||
specific cluster (or service account in the future). Attestations are managed
|
||||
through Grafeas as a dedicated Kind `ATTESTATION`.
|
||||
|
||||
### Deployment Environments
|
||||
|
||||
Google Cloud Platform (GCP) uses projects and service accounts as security
|
||||
boundaries. In addition to that, GKE uses clusters as a security boundary (a
|
||||
Kubernetes cluster can have its own secrets for example). These are some of the
|
||||
deployment targets that we plan to support as subjects of BinAuthz policy
|
||||
requirements: project, service account, cluster.
|
||||
|
||||
### Policy Management
|
||||
|
||||
Key concepts of BinAuthz are Attestation Authority and Policy, realized as REST
|
||||
resources managed through a REST API. An Attestation Authority is a named entity
|
||||
which has the power to create attestations. As a REST resource, it encapsulates
|
||||
the location of its attestations (where to store and retrieve from), as well as
|
||||
verification criteria (what makes an attestation valid). A Policy then names
|
||||
Attestation Authorities (whose attestations are) required to deploy an artifact
|
||||
to some target.
|
||||
|
||||
#### Example
|
||||
|
||||
This might be an Attestation Authority which represents an organization’s secure
|
||||
build system.
|
||||
|
||||
```
|
||||
{
|
||||
“name”: “projects/secure-builder/attestationAuthorities/built-securely”
|
||||
“public_keys”: <list of keys expected to sign this authority’s attestations>
|
||||
}
|
||||
```
|
||||
|
||||
A policy which requires securely built artifacts to deploy to the `prod` cluster
|
||||
may then look like this.
|
||||
|
||||
```
|
||||
{
|
||||
“cluster_admission_requirements”: {
|
||||
“cluster_name”: “prod”,
|
||||
“attestation_requirements”: [ “projects/secure-builder/attestationAuthorities/built-securely” ]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Attestations via Component Metadata API / Grafeas
|
||||
|
||||
Attestations are represented as Component Metadata objects. Grafeas is its open
|
||||
source sister project, and can be used as attestation transport too. An
|
||||
Attestation Authority names a Note (of Kind `ATTESTATION`) which is used as an
|
||||
anchor for this authority’s attestations, and optionally specifies public keys
|
||||
if attestations must be signed. Attestations by this authority are then
|
||||
represented as Occurrences attached to the authority’s Note.
|
||||
|
||||
### Enforcement Module Entry Point
|
||||
|
||||
Kubernetes orchestrates the execution of containers, predominantly focusing on
|
||||
Docker as the container runtime. Pod is the lowest level abstraction of a
|
||||
running container. Users can create Pods directly, or users can create
|
||||
Controllers (such as ReplicaSet) which then mediate Pod creation. We chose Pod
|
||||
admission as the interception point. At Pod admission time, information which BinAuthz
|
||||
needs is available: artifact identifier (container image URL), deploy target
|
||||
(project, service account, cluster). And Pod creation is the chokepoint through
|
||||
which flow all code paths to run a Docker container. Intercepting at Pod
|
||||
creation has some consequences which we must tolerate. Notably, a user may
|
||||
create a Controller, which then creates and manages Pods asynchronously from
|
||||
user’s original request. Because BinAuthz intercepts Pod creation, but not
|
||||
Container creation, we don't report a BinAuthz failure at Controller creation
|
||||
time, but only later, asynchronously from user’s action. Longer-term we plan to
|
||||
support intercepting Controller creation too for a better user experience
|
||||
(clearer errors immediately delivered). Even then we will have to keep the Pod
|
||||
enforcement, to ensure that community contributed controllers don’t accidentally
|
||||
bypass enforcement. Image policy webhook is a Kubernetes admission control
|
||||
webhook which allows delegating Pod admission decisions to a web service. We
|
||||
implement an enforcement service and configure the webhook to point to the
|
||||
service.
|
||||
|
||||
### Artifact Identification via Early Tag Resolution
|
||||
|
||||
A Docker container image is identified by the registry where it is stored,
|
||||
repository within the registry, and either tag or digest. Tags or digests are
|
||||
usually used for versioning. A digest uniquely and immutably identifies a
|
||||
container image. A tag, by contrast, may be associated to any digest, and this
|
||||
association may change over time. We chose to allow only digest based container
|
||||
images in BinAuthz. That is, when deploying to an environment which is subject
|
||||
to BinAuthz, a tag based deployment is automatically disallowed as it is
|
||||
impossible to decide the actual version that will be used once the Pod is
|
||||
created. Besides giving clearer BinAuthz semantics, we believe that digest based
|
||||
deployments are better production hygiene and thus were favored strongly by
|
||||
customers we worked with. As an exception to this rule, our policy language
|
||||
allows glob-based container image whitelisting, through which even tag based
|
||||
deployments may be allowed. This is useful for images that don’t go through the
|
||||
same internal vetting process (e.g., various sidecar containers). Longer-term
|
||||
this should be solved through exchange of attestations with the image providers.
|
||||
|
||||
## Conclusion and Next Steps
|
||||
|
||||
Binary Authorization enables centralized control over software release cycle.
|
||||
Stakeholders configure policies to enforce the requirements of the release
|
||||
process, gaining confidence that software is delivered to customers only if it
|
||||
meets the organization’s requirements. Attestations - trusted metadata
|
||||
associated to software artifacts - are used to assert that software meets
|
||||
specified requirements.
|
||||
|
||||
### Generalizing to Other Orchestration Systems
|
||||
|
||||
We described our first, GKE specific, implementation of BinAuthz, however we
|
||||
note that the basic principles apply to a variety of orchestration systems. We
|
||||
plan to support other GCP platforms (such as App Engine and App Engine Flex) in
|
||||
the future. Furthermore, an open specification of BinAuthz is forthcoming.
|
||||
|
||||
### Richer Policies and Attestations
|
||||
|
||||
BinAuthz attestation is like a seal stamp: its only meaning is “authority `X`
|
||||
attests artifact `Y`”. A richer language of statements, such as “authority `X`
|
||||
attests that artifact `Y` was built from source code `Z`” allows for more
|
||||
expressive policies and more meaningful control. We plan to extend the data
|
||||
model and policy language to support such richer statements.
|
||||
|
||||
### Toolchain Integration
|
||||
|
||||
In its current form, BinAuthz requires custom integration into organizations’
|
||||
workflows. We will reduce the integration cost by working with partners to
|
||||
support BinAuthz in their products. For example, CI/CD providers are of
|
||||
particular interest. A CI/CD pipeline may at different stages produce various
|
||||
attestations for the artifacts which it creates, and BinAuthz policy then
|
||||
enforce that proper process was followed. Source control system is another
|
||||
integration point, especially if Binary Authorization is to be based on source
|
||||
provenance of artifacts. To strengthen such guarantees, a trusted build system
|
||||
may be needed.
|
29
vendor/github.com/grafeas/grafeas/code-of-conduct.md
generated
vendored
Normal file
29
vendor/github.com/grafeas/grafeas/code-of-conduct.md
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
## Grafeas Code of Conduct
|
||||
|
||||
At Google, we recognize and celebrate the creativity and collaboration of open source contributors and the diversity of skills, experiences, cultures, and opinions they bring to the projects and communities they participate in.
|
||||
|
||||
Every one of Google's open source projects and communities are inclusive environments, based on treating all individuals respectfully, regardless of gender identity and expression, sexual orientation, disabilities, neurodiversity, physical appearance, body size, ethnicity, nationality, race, age, religion, or other protected category.
|
||||
|
||||
We value diverse opinions, but we value respectful behavior more.
|
||||
|
||||
Respectful behavior includes:
|
||||
* Being considerate, kind, constructive, and helpful.
|
||||
* Not engaging in demeaning, discriminatory, harassing, hateful, sexualized, or physically threatening behavior, speech, and imagery.
|
||||
* Not engaging in unwanted physical contact.
|
||||
|
||||
Some Google open source projects may adopt their own specific codes of conduct, which may have additional detailed expectations for participants.
|
||||
|
||||
### Resolve Peacefully
|
||||
|
||||
We do not believe that all conflict is bad; healthy debate and disagreement often yield positive results. However, it is never okay to be disrespectful or to engage in behavior that violates the project’s code of conduct.
|
||||
|
||||
If you see someone violating the code of conduct, you are encouraged to address the behavior directly with those involved. Many issues can be resolved quickly and easily, and this gives people more control over the outcome of their dispute. If you are unable to resolve the matter for any reason, or if the behavior is threatening or harassing, report it. We are dedicated to providing an environment where participants feel welcome and safe.
|
||||
|
||||
Reports should be directed to wmd@google.com, the Project Steward for Grafeas. It is the Project Steward’s duty to receive and address reported violations of the code of conduct. The steward will then work with a committee consisting of representatives from the Open Source Programs Office and the Google Open Source Strategy team.
|
||||
|
||||
We will investigate every complaint, but you may not receive a direct response. We will use our discretion in determining when and how to follow up on reported incidents, which may range from not taking action to permanent expulsion from the project and project-sponsored spaces. We will notify the accused of the report and provide them an opportunity to discuss it before any action is taken. In potentially harmful situations, such as ongoing harassment or threats to anyone's safety, we may take action without notice. We will do our utmost to keep the identity of the reporter anonymous.
|
||||
|
||||
|
||||
This enforcement policy has been adopted from [IndieWeb Code of Conduct](https://indieweb.org/code-of-conduct).
|
||||
|
||||
|
22
vendor/github.com/grafeas/grafeas/config.yaml.sample
generated
vendored
Normal file
22
vendor/github.com/grafeas/grafeas/config.yaml.sample
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright 2017 The Grafeas 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.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
grafeas:
|
||||
server:
|
||||
# Endpoint address, e.g. localhost:10000
|
||||
address: localhost:10000
|
||||
# PKI configuration (optional)
|
||||
cafile:
|
||||
keyfile:
|
||||
certfile:
|
159
vendor/github.com/grafeas/grafeas/docs/running_grafeas.md
generated
vendored
Normal file
159
vendor/github.com/grafeas/grafeas/docs/running_grafeas.md
generated
vendored
Normal file
@ -0,0 +1,159 @@
|
||||
# Running Grafeas
|
||||
|
||||
## Start Grafeas
|
||||
|
||||
To start the server go to `samples/server/go-server/api/server/main` and execute
|
||||
|
||||
go run main.go
|
||||
|
||||
This will start the Grafeas gRPC and REST API:s on `localhost:10000`. To start grafeas with a custom configuration use the `-config` flag (e.g. `-config config.yaml`). The root directory includes a `config.yaml.sample` that can be used as a starting point when creating your own config file.
|
||||
|
||||
### Access REST API with curl
|
||||
|
||||
Grafeas provides both a REST API and a gRPC API. Here is an example of using the REST API to list projects in Grafeas.
|
||||
|
||||
`curl http://localhost:10000/v1alpha1/projects`
|
||||
|
||||
### Access gRPC API with a go client
|
||||
|
||||
Below is a small example of a go client that connects to grafeas and outputs any notes in `myproject`
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
pb "github.com/grafeas/grafeas/v1alpha1/proto"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conn, err := grpc.Dial("localhost:10000", grpc.WithInsecure())
|
||||
defer conn.Close()
|
||||
client := pb.NewGrafeasClient(conn)
|
||||
// List notes
|
||||
resp, err := client.ListNotes(context.Background(),
|
||||
&pb.ListNotesRequest{
|
||||
Parent: "projects/myproject",
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if len(resp.Notes) != 0 {
|
||||
log.Println(resp.Notes)
|
||||
} else {
|
||||
log.Println("Project does not contain any notes")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Use Grafeas with self-signed certificate
|
||||
|
||||
### Generate CA, keys and certs
|
||||
|
||||
_NOTE: The steps described in this section is meant for development environments._
|
||||
|
||||
```
|
||||
# Create CA
|
||||
openssl genrsa -out ca.key 2048
|
||||
# make sure to set Common Name to your domain, e.g. localhost (without port)
|
||||
openssl req -new -x509 -days 365 -key ca.key -out ca.crt
|
||||
|
||||
# Create the Client Key and CSR
|
||||
openssl genrsa -out client.key 2048
|
||||
openssl req -new -key client.key -out client.csr
|
||||
|
||||
# Create self-signed client cert
|
||||
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
|
||||
|
||||
# Convert Client Key to PKCS
|
||||
openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12
|
||||
|
||||
# Convert Client Key to (combined) PEM
|
||||
openssl pkcs12 -in client.p12 -out client.pem -clcerts
|
||||
```
|
||||
|
||||
This is basically following https://gist.github.com/mtigas/952344 with some tweaks
|
||||
|
||||
### Update config
|
||||
|
||||
Add the following to your config file
|
||||
|
||||
cafile: ca.crt
|
||||
keyfile: ca.key
|
||||
certfile: ca.crt
|
||||
|
||||
### Access REST API with curl
|
||||
|
||||
When using curl with a self signed certificate you need to add `-k/--insecure` and specify the client certificate.
|
||||
|
||||
`curl -k --cert path/to/client.pem https://localhost:10000/v1alpha1/projects`
|
||||
|
||||
### Access gRPC with a go client
|
||||
|
||||
When using a go client to access Grafeas with a self signed certificate you need to specify the client certificate, client key and the CA certificate.
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
||||
pb "github.com/grafeas/grafeas/v1alpha1/proto"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
var (
|
||||
certFile = "/path/to/client.crt"
|
||||
keyFile = "/path/to/client.key"
|
||||
caFile = "/path/to/ca.crt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Load client cert
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load CA cert
|
||||
caCert, err := ioutil.ReadFile(caFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
caCertPool.AppendCertsFromPEM(caCert)
|
||||
|
||||
// Setup HTTPS client
|
||||
tlsConfig := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
RootCAs: caCertPool,
|
||||
}
|
||||
tlsConfig.BuildNameToCertificate()
|
||||
creds := credentials.NewTLS(tlsConfig)
|
||||
conn, err := grpc.Dial("localhost:10000", grpc.WithTransportCredentials(creds))
|
||||
client := pb.NewGrafeasClient(conn)
|
||||
|
||||
// List notes
|
||||
resp, err := client.ListNotes(context.Background(),
|
||||
&pb.ListNotesRequest{
|
||||
Parent: "projects/myproject",
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if len(resp.Notes) != 0 {
|
||||
log.Println(resp.Notes)
|
||||
} else {
|
||||
log.Println("Project does not contain any notes")
|
||||
}
|
||||
}
|
||||
```
|
21
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/README.md
generated
vendored
Normal file
21
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/README.md
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Grafeas API Reference Implementation
|
||||
|
||||
This is a reference implementation of the [Grafeas API Spec](https://github.com/Grafeas/Grafeas/blob/master/README)
|
||||
|
||||
## Overview
|
||||
|
||||
This reference implementation comes with the following caveats:
|
||||
* Storage: map backed in memory server storage
|
||||
* No ACLs are used in this implementation
|
||||
* No authorization is in place #28
|
||||
* Filtering in list methods is not currently supported #29
|
||||
* Operation names are not currently validated when tied to notes/occurrences #31
|
||||
|
||||
|
||||
### Running the server
|
||||
To run the server, follow these simple steps:
|
||||
|
||||
```
|
||||
go run main/main.go
|
||||
```
|
||||
|
1
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/app.yaml
generated
vendored
Normal file
1
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/app.yaml
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
application:
|
62
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/config/config.go
generated
vendored
Normal file
62
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/config/config.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2017 The Grafeas 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/grafeas/grafeas/samples/server/go-server/api/server/server"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// File is the grafeas config file.
|
||||
type file struct {
|
||||
Grafeas *config `yaml:"grafeas"`
|
||||
}
|
||||
|
||||
// Config is the global configuration for an instance of Grafeas.
|
||||
type config struct {
|
||||
Server *server.Config `yaml:"server"`
|
||||
}
|
||||
|
||||
// DefaultConfig is a configuration that can be used as a fallback value.
|
||||
func defaultConfig() *config {
|
||||
return &config{
|
||||
&server.Config{
|
||||
Address: "localhost:10000",
|
||||
CertFile: "",
|
||||
KeyFile: "",
|
||||
CAFile: "",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a config from a YAML-file. If fileName is an empty
|
||||
// string a default config will be returned.
|
||||
func LoadConfig(fileName string) (*config, error) {
|
||||
if fileName == "" {
|
||||
return defaultConfig(), nil
|
||||
}
|
||||
data, err := ioutil.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var configFile file
|
||||
err = yaml.Unmarshal(data, &configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return configFile.Grafeas, nil
|
||||
}
|
36
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/main/main.go
generated
vendored
Normal file
36
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/main/main.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2017 The Grafeas 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
|
||||
"github.com/grafeas/grafeas/samples/server/go-server/api/server/config"
|
||||
"github.com/grafeas/grafeas/samples/server/go-server/api/server/server"
|
||||
)
|
||||
|
||||
var (
|
||||
configFile = flag.String("config", "", "Path to a config file")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
config, err := config.LoadConfig(*configFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load config file")
|
||||
}
|
||||
server.Run(config.Server)
|
||||
}
|
218
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/name/name.go
generated
vendored
Normal file
218
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/name/name.go
generated
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
// Copyright 2017 The Grafeas 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// package name provides methods for manipulating resource names.
|
||||
package name
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// ResourceKind is the type that will be used for all public resource kinds.
|
||||
type ResourceKind string
|
||||
|
||||
const (
|
||||
// Position of projectID in name string
|
||||
projectKeywordIndex = 1
|
||||
// Position of the resourceID in the string
|
||||
resourceKeywordIndex = 3
|
||||
projectsKeyword = "projects"
|
||||
occurrencesKeyword = "occurrences"
|
||||
notesKeyword = "notes"
|
||||
operationsKeyword = "operations"
|
||||
|
||||
// Note is the ResourceKind associated with notes.
|
||||
Note = ResourceKind(notesKeyword)
|
||||
// Occurrence is the ResourceKind associated with occurrences.
|
||||
Occurrence = ResourceKind(occurrencesKeyword)
|
||||
// Operation is the ResourceKind associated with operations.
|
||||
Operation = ResourceKind(operationsKeyword)
|
||||
// Unknown is the ResourceKind when the kind cannot be determined.
|
||||
Unknown = ResourceKind("")
|
||||
|
||||
// NoCharLimit is used to signal that no resource id length validation is needed
|
||||
NoCharLimit = -1
|
||||
)
|
||||
|
||||
var (
|
||||
projectNameFormat = FormatProject("{project_id}")
|
||||
occurrenceNameFormat = FormatOccurrence("{project_id}", "{occurrence_id}")
|
||||
operationNameFormat = FormatOperation("{provider_project_id}", "{operation_id}")
|
||||
noteNameFormat = FormatNote("{provider_project_id}", "{note_id}")
|
||||
)
|
||||
|
||||
func invalidArg(pattern, got string) error {
|
||||
return status.Error(codes.InvalidArgument, fmt.Sprintf("expected name to be of form %q, input was %v", pattern, got))
|
||||
}
|
||||
|
||||
// FormatProject synthesizes a stringly typed name of the form:
|
||||
// projects/{project_id}
|
||||
// See also: ParseProject
|
||||
func FormatProject(projectID string) string {
|
||||
return fmt.Sprintf("%v/%v", projectsKeyword, projectID)
|
||||
}
|
||||
|
||||
func OccurrenceName(pID, oID string) string {
|
||||
return fmt.Sprintf("projects/%v/occurrences/%v", pID, oID)
|
||||
}
|
||||
|
||||
func OperationName(pID, oID string) string {
|
||||
return fmt.Sprintf("projects/%v/operations/%v", pID, oID)
|
||||
}
|
||||
|
||||
func NoteName(pID, nID string) string {
|
||||
return fmt.Sprintf("projects/%v/notes/%v", pID, nID)
|
||||
}
|
||||
|
||||
// FormatNoteProjectKW synthesizes a stringly typed name of the form:
|
||||
// projects/{project_id}/notes/{note_id}
|
||||
// See also: ParseNote
|
||||
func FormatNote(projectID, noteID string) string {
|
||||
return strings.Join([]string{projectsKeyword, projectID, string(Note), noteID}, "/")
|
||||
}
|
||||
|
||||
// FormatOccurrence synthesizes a stringly typed name of the form:
|
||||
// projects/{project_id}/occurrences/{occurrence_id}
|
||||
// See also: ParseOccurrence
|
||||
func FormatOccurrence(projectID, occurrenceID string) string {
|
||||
return strings.Join([]string{projectsKeyword, projectID, string(Occurrence), occurrenceID}, "/")
|
||||
}
|
||||
|
||||
// FormatOperation synthesizes a stringly typed name of the form:
|
||||
// providers/{provider_id}/project/{project_id}/operations/{operation_id}
|
||||
// See also: ParseOperation
|
||||
func FormatOperation(projectID, operationID string) string {
|
||||
return strings.Join([]string{projectsKeyword, projectID, operationsKeyword, operationID}, "/")
|
||||
}
|
||||
|
||||
// ParseResourceKindAndResource takes a stringly typed name of the form:
|
||||
// projects/{project_id}/occurrences/{occurrence_id}
|
||||
// projects/{project_name}/notes/{note_id}
|
||||
// or:
|
||||
// validates form and returns either an error or the ResourceKind
|
||||
// (either occurrence or note) and project/resource-ids
|
||||
func ParseResourceKindAndResource(name string) (ResourceKind, string, string, error) {
|
||||
err := invalidArg(fmt.Sprintf("%q or %q", occurrenceNameFormat, noteNameFormat), name)
|
||||
params := strings.Split(name, "/")
|
||||
if len(params) != 4 {
|
||||
return Unknown, "", "", err
|
||||
}
|
||||
switch params[projectKeywordIndex-1] {
|
||||
case projectsKeyword:
|
||||
switch params[resourceKeywordIndex-1] {
|
||||
case string(Occurrence):
|
||||
return Occurrence, params[projectKeywordIndex], params[resourceKeywordIndex], nil
|
||||
case string(Note):
|
||||
return Note, params[projectKeywordIndex], params[resourceKeywordIndex], nil
|
||||
case string(Operation):
|
||||
return Operation, params[projectKeywordIndex], params[resourceKeywordIndex], nil
|
||||
}
|
||||
|
||||
return Unknown, "", "", invalidArg(fmt.Sprintf("%q or %q", occurrenceNameFormat, noteNameFormat), name)
|
||||
}
|
||||
return Unknown, "", "", err
|
||||
}
|
||||
|
||||
// ParseResourceKindAndProjectFromPath retrieves a projectID and resource kind from a Grafeas URL path
|
||||
// This method should be used with CreateRequests.
|
||||
func ParseResourceKindAndProject(parent string) (ResourceKind, string, error) {
|
||||
err := invalidArg(fmt.Sprintf("%q or %q", occurrenceNameFormat, noteNameFormat), parent)
|
||||
params := strings.Split(parent, "/")
|
||||
if len(params) != 3 {
|
||||
return Unknown, "", err
|
||||
}
|
||||
|
||||
switch params[projectKeywordIndex-1] {
|
||||
case projectsKeyword:
|
||||
switch params[resourceKeywordIndex-1] {
|
||||
case string(Occurrence):
|
||||
return Occurrence, params[projectKeywordIndex], nil
|
||||
case string(Note):
|
||||
return Note, params[projectKeywordIndex], nil
|
||||
case string(Operation):
|
||||
return Operation, params[projectKeywordIndex], nil
|
||||
}
|
||||
|
||||
return Unknown, "", invalidArg(fmt.Sprintf("%q, %q, or %q", occurrenceNameFormat,
|
||||
noteNameFormat, operationNameFormat), parent)
|
||||
}
|
||||
return Unknown, "", err
|
||||
}
|
||||
|
||||
// ParseOccurrence takes a stringly typed name of the form:
|
||||
// projects/{project_id}/occurrences/{occurrence_id}
|
||||
// validates its form and returns either an error or the project-/occurrence-ids.
|
||||
func ParseOccurrence(name string) (string, string, error) {
|
||||
return parseProjectAndEntityID(name, projectsKeyword, occurrencesKeyword, NoCharLimit)
|
||||
}
|
||||
|
||||
// ParseNote takes a stringly typed name of the forms:
|
||||
// providers/{provider_name}/notes/{note_id}
|
||||
// providers/{provider_name}/notes/{note_id}
|
||||
// validates its form and returns either an error or the provider-/note-ids.
|
||||
func ParseNote(name string) (string, string, error) {
|
||||
return parseProjectAndEntityID(name, projectsKeyword, notesKeyword, 100)
|
||||
}
|
||||
|
||||
// ParseOperation takes a stringly typed name of the form:
|
||||
// projects/{project_id}/operations/{operation_id}
|
||||
// validates its form and returns either an error or the project-/operation-ids
|
||||
func ParseOperation(name string) (string, string, error) {
|
||||
return parseProjectAndEntityID(name, projectsKeyword, operationsKeyword, 100)
|
||||
}
|
||||
|
||||
// parseProjectAndEntityID takes resource and project keywords, a max resource id length and a stringly typed name of the form:
|
||||
// projects/{project_id}/<resourceKeyword>/{entity_id}
|
||||
// validates its form and returns either an error or the project and resource ids. Only validates maxResourceIDLength if it is greater than 0
|
||||
func parseProjectAndEntityID(name, projectKeyword, resourceKeyword string, maxResourceIDLength int) (string, string, error) {
|
||||
format := fmt.Sprintf("%s/{project_id}/%s/{entity_id}", projectKeyword, resourceKeyword)
|
||||
params := strings.Split(name, "/")
|
||||
if len(params) != 4 {
|
||||
return "", "", invalidArg(format, name)
|
||||
}
|
||||
if params[projectKeywordIndex-1] != projectKeyword {
|
||||
return "", "", invalidArg(format, name)
|
||||
}
|
||||
if params[resourceKeywordIndex-1] != resourceKeyword {
|
||||
return "", "", invalidArg(format, name)
|
||||
}
|
||||
if params[projectKeywordIndex] == "" || params[resourceKeywordIndex] == "" {
|
||||
return "", "", invalidArg(format, name)
|
||||
}
|
||||
if maxResourceIDLength > 0 && len(params[resourceKeywordIndex]) > maxResourceIDLength {
|
||||
return "", "", status.Error(codes.InvalidArgument, fmt.Sprintf("resource id must be <= %v characters. Input was %v", maxResourceIDLength, name))
|
||||
}
|
||||
return params[projectKeywordIndex], params[resourceKeywordIndex], nil
|
||||
}
|
||||
|
||||
// ParseProject takes a stringly typed name of the form:
|
||||
// projects/{project_id}
|
||||
// validates its form and returns either an error or the project-id.
|
||||
func ParseProject(name string) (string, error) {
|
||||
params := strings.Split(name, "/")
|
||||
if len(params) != 2 {
|
||||
return "", invalidArg(projectNameFormat, name)
|
||||
}
|
||||
if params[projectKeywordIndex-1] != projectsKeyword {
|
||||
return "", invalidArg(projectNameFormat, name)
|
||||
}
|
||||
if params[projectKeywordIndex] == "" {
|
||||
return "", invalidArg(projectNameFormat, name)
|
||||
}
|
||||
return params[projectKeywordIndex], nil
|
||||
}
|
255
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/name/name_test.go
generated
vendored
Normal file
255
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/name/name_test.go
generated
vendored
Normal file
@ -0,0 +1,255 @@
|
||||
// Copyright 2017 The Grafeas 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package name
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNameRoundtrips(t *testing.T) {
|
||||
tests := []struct {
|
||||
part1 string
|
||||
part2 string
|
||||
}{
|
||||
{"a", "b"},
|
||||
{"foo", "foo"},
|
||||
{"blah-foo", "baz-inga"},
|
||||
}
|
||||
|
||||
// Test two-part names
|
||||
for _, test := range tests {
|
||||
|
||||
on := FormatOccurrence(test.part1, test.part2)
|
||||
if p1, p2, err := ParseOccurrence(on); err != nil {
|
||||
t.Errorf("ParseOccurrence %v; want (%v, %v), got error %v",
|
||||
on, test.part1, test.part2, err)
|
||||
} else if p1 != test.part1 || p2 != test.part2 {
|
||||
t.Errorf("ParseOccurrence %v; want (%v, %v), got (%v, %v)",
|
||||
on, test.part1, test.part2, p1, p2)
|
||||
}
|
||||
if rt, p1, p2, err := ParseResourceKindAndResource(on); err != nil {
|
||||
t.Errorf("ParseResourceKindAndResource %v; want (%v, %v, %v), got error %v",
|
||||
on, Occurrence, test.part1, test.part2, err)
|
||||
} else if rt != Occurrence || p1 != test.part1 || p2 != test.part2 {
|
||||
t.Errorf("ParseResourceKindAndResource %v; want (%v, %v, %v), got (%v, %v, %v)",
|
||||
on, Occurrence, test.part1, test.part2, rt, p1, p2)
|
||||
}
|
||||
|
||||
if rt, p1, p2, err := ParseResourceKindAndResource(on); err != nil {
|
||||
t.Errorf("ParseResourceKindAndResource %v; want (%v, %v, %v), got error %v",
|
||||
on, Occurrence, test.part1, test.part2, err)
|
||||
} else if rt != Occurrence || p1 != test.part1 || p2 != test.part2 {
|
||||
t.Errorf("ParseResourceKindAndResource %v; want (%v, %v, %v), got (%v, %v, %v)",
|
||||
on, Occurrence, test.part1, test.part2, rt, p1, p2)
|
||||
}
|
||||
|
||||
nn := FormatNote(test.part1, test.part2)
|
||||
if p1, p2, err := ParseNote(nn); err != nil {
|
||||
t.Errorf("ParseNote %v; want (%v, %v), got error %v",
|
||||
nn, test.part1, test.part2, err)
|
||||
} else if p1 != test.part1 || p2 != test.part2 {
|
||||
t.Errorf("ParseNote %v; want (%v, %v), got (%v, %v)",
|
||||
nn, test.part1, test.part2, p1, p2)
|
||||
}
|
||||
if rt, p1, p2, err := ParseResourceKindAndResource(nn); err != nil {
|
||||
t.Errorf("ParseResourceKindAndResource %v; want (%v, %v, %v), got error %v",
|
||||
on, Note, test.part1, test.part2, err)
|
||||
} else if rt != Note || p1 != test.part1 || p2 != test.part2 {
|
||||
t.Errorf("ParseResourceKindAndResource %v; want (%v, %v, %v), got (%v, %v, %v)",
|
||||
on, Note, test.part1, test.part2, rt, p1, p2)
|
||||
}
|
||||
|
||||
opn := FormatOperation(test.part1, test.part2)
|
||||
if p1, p2, err := ParseOperation(opn); err != nil {
|
||||
t.Errorf("ParseOperation %v; got error %v, want (%v, %v)",
|
||||
opn, err, test.part1, test.part2)
|
||||
} else if p1 != test.part1 || p2 != test.part2 {
|
||||
t.Errorf("ParseOperation %v; got (%v, %v), want (%v, %v)",
|
||||
opn, p1, p2, test.part1, test.part2)
|
||||
}
|
||||
}
|
||||
|
||||
// Test one-part names
|
||||
for _, test := range tests {
|
||||
fn := FormatProject(test.part1)
|
||||
if p1, err := ParseProject(fn); err != nil {
|
||||
t.Errorf("ParseProject %v; want %v, got error %v",
|
||||
fn, test.part1, err)
|
||||
} else if p1 != test.part1 {
|
||||
t.Errorf("ParseProject %v; want %v, got %v",
|
||||
fn, test.part1, p1)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNoteValidation(t *testing.T) {
|
||||
badNoteNames := []string{
|
||||
// Bad keyword
|
||||
"providers/foo/findings/bar",
|
||||
// Too few parts
|
||||
"providers/foo/notes",
|
||||
// Too many parts
|
||||
"providers/foo/notes/bar/baz",
|
||||
// Empty part
|
||||
"providers//notes/bar",
|
||||
"providers/foo/notes/",
|
||||
// Too long
|
||||
"providers/foo/occurrences/" + strings.Repeat("a", 101),
|
||||
}
|
||||
|
||||
for _, test := range badNoteNames {
|
||||
if p1, p2, err := ParseNote(test); err == nil {
|
||||
t.Errorf("ParseNote %v; wanted error, got (%v, %v)",
|
||||
test, p1, p2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseOccurrenceValidation(t *testing.T) {
|
||||
badOccurrenceNames := []string{
|
||||
// Bad keyword
|
||||
"providers/foo/occurrences/bar",
|
||||
// Bad keyword
|
||||
"projects/foo/results/bar",
|
||||
// Too few parts
|
||||
"projects/foo/occurrences",
|
||||
// Too many parts
|
||||
"projects/foo/occurrences/bar/baz",
|
||||
// Empty part
|
||||
"projects//occurrences/bar",
|
||||
"projects/foo/occurrences/",
|
||||
}
|
||||
|
||||
for _, test := range badOccurrenceNames {
|
||||
if p1, p2, err := ParseOccurrence(test); err == nil {
|
||||
t.Errorf("ParseOccurrence %v; wanted error, got (%v, %v)",
|
||||
test, p1, p2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResourceKindAndResource(t *testing.T) {
|
||||
badResourceNames := []string{
|
||||
"providers/foo/findings/bar",
|
||||
"providers/foo/occurrences/bar",
|
||||
"foo/foo/bar/bar",
|
||||
"projects/foo/results/bar",
|
||||
"projects/foo/results",
|
||||
"projects/foo/notes",
|
||||
"projects/foo",
|
||||
"providers/foo/results",
|
||||
"providers/foo/notes",
|
||||
"providers/foo",
|
||||
"projects/foo/findings",
|
||||
"projects/foo",
|
||||
"projects/foo/occurrences",
|
||||
}
|
||||
for _, test := range badResourceNames {
|
||||
if t1, p, r, err := ParseResourceKindAndResource(test); err == nil {
|
||||
t.Errorf("ParseResourceTypeAndResource %v; wanted error, got (%v, %v, %v)",
|
||||
test, t1, p, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseOperations(t *testing.T) {
|
||||
badResourceNames := []string{
|
||||
"providers/foo/operations/bar",
|
||||
"providers/foo/operations/bar",
|
||||
"foo/foo/bar/bar",
|
||||
"projects/foo/providers/bar",
|
||||
"providers/foo/projects/bar",
|
||||
"projects/foo/providers/bar/operations/baz",
|
||||
"operations/foo",
|
||||
"providers/-/projects/-/operations/abc",
|
||||
"providers//projects//operations/",
|
||||
"providers/foo/projects/bar/operations/" + strings.Repeat("a", 101),
|
||||
}
|
||||
for _, test := range badResourceNames {
|
||||
if t1, r, err := ParseOperation(test); err == nil {
|
||||
t.Errorf("ParseOperation %v; got (%v, %v), wanted error",
|
||||
test, t1, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOccurrenceErrorMessage(t *testing.T) {
|
||||
want := "projects/{project_id}/occurrences/{entity_id}"
|
||||
if _, _, err := ParseOccurrence("providers/foo/notes/bar"); !strings.Contains(err.Error(), "projects/{project_id}/occurrences/{entity_id}") {
|
||||
t.Fatalf("bad error msg, got %q want it to contain %q", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResourceKindAndProjectPath(t *testing.T) {
|
||||
badResourcePaths := []string{
|
||||
"providers/foo/operations/bar",
|
||||
"providers/foo/operations/bar",
|
||||
"foo/foo/bar/bar",
|
||||
"projects/foo/providers/bar",
|
||||
"providers/foo/projects/bar",
|
||||
"projects/foo/providers/bar/operations/baz",
|
||||
"projects/foo/operations/bar",
|
||||
"projects/foo/occurrences/bar",
|
||||
"projects/foo/notes/bar",
|
||||
"operations/foo",
|
||||
"providers/-/projects/-/operations/abc",
|
||||
"providers//projects//operations/",
|
||||
"providers/foo/projects/bar/operations/" + strings.Repeat("a", 101),
|
||||
}
|
||||
for _, test := range badResourcePaths {
|
||||
if t1, r, err := ParseResourceKindAndProject(test); err == nil {
|
||||
t.Errorf("ParseResourceKindAndProject %v; got (%v, %v), wanted error",
|
||||
test, t1, r)
|
||||
}
|
||||
}
|
||||
|
||||
goodResourcePaths := []string{
|
||||
"projects/foo/occurrences",
|
||||
"projects/foo/operations",
|
||||
"projects/foo/notes",
|
||||
}
|
||||
for _, test := range goodResourcePaths {
|
||||
if t1, r, err := ParseResourceKindAndProject(test); err != nil {
|
||||
t.Errorf("ParseResourceKindAndProject %v; got (%v, %v, %v), wanted success",
|
||||
test, t1, r, err)
|
||||
} else if r != "foo" {
|
||||
t.Errorf("ParseResourceKindAndProject %v; got %v, wanted foo", test, t1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseProjectValidation(t *testing.T) {
|
||||
badProjectNames := []string{
|
||||
// Bad keyword
|
||||
"providers/foo/",
|
||||
// Trailing slash
|
||||
"projects/foo/",
|
||||
// Last part non-empty
|
||||
"projects/foo/baz",
|
||||
// Too many parts
|
||||
"projects/foo/baz/asdf",
|
||||
// Empty part
|
||||
"projects//",
|
||||
}
|
||||
|
||||
for _, test := range badProjectNames {
|
||||
if p1, err := ParseProject(test); err == nil {
|
||||
t.Errorf("ParseProject %v; wanted error, got %v",
|
||||
test, p1)
|
||||
}
|
||||
}
|
||||
}
|
207
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/server/server.go
generated
vendored
Normal file
207
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/server/server.go
generated
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
// Copyright 2017 The Grafeas 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/cmux"
|
||||
"github.com/grafeas/grafeas/samples/server/go-server/api/server/storage"
|
||||
"github.com/grafeas/grafeas/samples/server/go-server/api/server/v1alpha1"
|
||||
pb "github.com/grafeas/grafeas/v1alpha1/proto"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
opspb "google.golang.org/genproto/googleapis/longrunning"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Address string `yaml:"address"` // Endpoint address, e.g. localhost:10000
|
||||
CertFile string `yaml:"certfile"` // A PEM eoncoded certificate file
|
||||
KeyFile string `yaml:"keyfile"` // A PEM encoded private key file
|
||||
CAFile string `yaml:"cafile"` // A PEM eoncoded CA's certificate file
|
||||
}
|
||||
|
||||
// Run initializes grpc and grpc gateway api services on the same address
|
||||
func Run(config *Config) {
|
||||
l, err := net.Listen("tcp", config.Address)
|
||||
if err != nil {
|
||||
log.Fatalln("could not listen to address", config.Address)
|
||||
}
|
||||
log.Println("starting grpc server")
|
||||
|
||||
var (
|
||||
apiHandler http.Handler
|
||||
apiListener net.Listener
|
||||
srv *http.Server
|
||||
ctx = context.Background()
|
||||
httpMux = http.NewServeMux()
|
||||
tcpMux = cmux.New(l)
|
||||
)
|
||||
|
||||
tlsConfig, err := tlsClientConfig(config.CertFile)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to create tls config", err)
|
||||
}
|
||||
|
||||
if tlsConfig != nil {
|
||||
cert, err := tls.LoadX509KeyPair(config.CertFile, config.KeyFile)
|
||||
if err != nil {
|
||||
log.Fatalln("Failed to load certificate files", err)
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
tlsConfig.NextProtos = []string{"h2"}
|
||||
|
||||
apiListener = tls.NewListener(tcpMux.Match(cmux.Any()), tlsConfig)
|
||||
go func() { handleShutdown(tcpMux.Serve()) }()
|
||||
|
||||
grpcServer := newGrpcServer(tlsConfig)
|
||||
gwmux := newGrpcGatewayServer(ctx, apiListener.Addr().String(), tlsConfig)
|
||||
|
||||
httpMux.Handle("/", gwmux)
|
||||
apiHandler = grpcHandlerFunc(grpcServer, httpMux)
|
||||
|
||||
log.Println("grpc server is configured with client certificate authentication")
|
||||
} else {
|
||||
grpcL := tcpMux.Match(cmux.HTTP2HeaderField("content-type", "application/grpc"))
|
||||
apiListener = tcpMux.Match(cmux.Any())
|
||||
go func() { handleShutdown(tcpMux.Serve()) }()
|
||||
|
||||
grpcServer := newGrpcServer(nil)
|
||||
go func() { handleShutdown(grpcServer.Serve(grpcL)) }()
|
||||
|
||||
gwmux := newGrpcGatewayServer(ctx, apiListener.Addr().String(), nil)
|
||||
|
||||
httpMux.Handle("/", gwmux)
|
||||
apiHandler = httpMux
|
||||
|
||||
log.Println("grpc server is configured without client certificate authentication")
|
||||
}
|
||||
|
||||
srv = &http.Server{
|
||||
Handler: apiHandler,
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
|
||||
// blocking call
|
||||
handleShutdown(srv.Serve(apiListener))
|
||||
log.Println("Grpc API stopped")
|
||||
}
|
||||
|
||||
// handleShutdown handles the server shut down error.
|
||||
func handleShutdown(err error) {
|
||||
if err != nil {
|
||||
if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newGrpcServer(tlsConfig *tls.Config) *grpc.Server {
|
||||
grpcOpts := []grpc.ServerOption{}
|
||||
|
||||
if tlsConfig != nil {
|
||||
grpcOpts = append(grpcOpts, grpc.Creds(credentials.NewTLS(tlsConfig)))
|
||||
}
|
||||
|
||||
grpcServer := grpc.NewServer(grpcOpts...)
|
||||
g := v1alpha1.Grafeas{S: storage.NewMemStore()}
|
||||
pb.RegisterGrafeasServer(grpcServer, &g)
|
||||
pb.RegisterGrafeasProjectsServer(grpcServer, &g)
|
||||
opspb.RegisterOperationsServer(grpcServer, &g)
|
||||
|
||||
return grpcServer
|
||||
}
|
||||
|
||||
func newGrpcGatewayServer(ctx context.Context, listenerAddr string, tlsConfig *tls.Config) http.Handler {
|
||||
var (
|
||||
gwTLSConfig *tls.Config
|
||||
gwOpts []grpc.DialOption
|
||||
)
|
||||
|
||||
if tlsConfig != nil {
|
||||
gwTLSConfig = tlsConfig.Clone()
|
||||
gwTLSConfig.InsecureSkipVerify = true
|
||||
gwOpts = append(gwOpts, grpc.WithTransportCredentials(credentials.NewTLS(gwTLSConfig)))
|
||||
} else {
|
||||
gwOpts = append(gwOpts, grpc.WithInsecure())
|
||||
}
|
||||
|
||||
// changes json serializer to include empty fields with default values
|
||||
jsonOpt := runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{EmitDefaults: true})
|
||||
gwmux := runtime.NewServeMux(jsonOpt)
|
||||
|
||||
conn, err := grpc.DialContext(ctx, listenerAddr, gwOpts...)
|
||||
if err != nil {
|
||||
log.Fatal("could not initialize grpc gateway connection")
|
||||
}
|
||||
err = pb.RegisterGrafeasHandler(ctx, gwmux, conn)
|
||||
if err != nil {
|
||||
log.Fatal("could not initialize ancestry grpc gateway")
|
||||
}
|
||||
|
||||
err = pb.RegisterGrafeasProjectsHandler(ctx, gwmux, conn)
|
||||
if err != nil {
|
||||
log.Fatal("could not initialize notification grpc gateway")
|
||||
}
|
||||
|
||||
return http.Handler(gwmux)
|
||||
}
|
||||
|
||||
// grpcHandlerFunc returns an http.Handler that delegates to grpcServer on incoming gRPC
|
||||
// connections or otherHandler otherwise. Copied from cockroachdb.
|
||||
func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
|
||||
grpcServer.ServeHTTP(w, r)
|
||||
} else {
|
||||
otherHandler.ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// tlsClientConfig initializes a *tls.Config using the given CA. The resulting
|
||||
// *tls.Config is meant to be used to configure an HTTP server to do client
|
||||
// certificate authentication.
|
||||
//
|
||||
// If no CA is given, a nil *tls.Config is returned; no client certificate will
|
||||
// be required and verified. In other words, authentication will be disabled.
|
||||
func tlsClientConfig(caPath string) (*tls.Config, error) {
|
||||
if caPath == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
caCert, err := ioutil.ReadFile(caPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
caCertPool := x509.NewCertPool()
|
||||
caCertPool.AppendCertsFromPEM(caCert)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
ClientCAs: caCertPool,
|
||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||
}
|
||||
|
||||
return tlsConfig, nil
|
||||
}
|
307
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/storage/memstore.go
generated
vendored
Normal file
307
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/storage/memstore.go
generated
vendored
Normal file
@ -0,0 +1,307 @@
|
||||
// Copyright 2017 The Grafeas 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES 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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/grafeas/grafeas/samples/server/go-server/api/server/name"
|
||||
"github.com/grafeas/grafeas/server-go"
|
||||
pb "github.com/grafeas/grafeas/v1alpha1/proto"
|
||||
opspb "google.golang.org/genproto/googleapis/longrunning"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// memStore is an in-memory storage solution for Grafeas
|
||||
type memStore struct {
|
||||
sync.RWMutex
|
||||
occurrencesByID map[string]*pb.Occurrence
|
||||
notesByID map[string]*pb.Note
|
||||
opsByID map[string]*opspb.Operation
|
||||
projects map[string]bool
|
||||
}
|
||||
|
||||
// NewMemStore creates a memStore with all maps initialized.
|
||||
func NewMemStore() server.Storager {
|
||||
return &memStore{
|
||||
occurrencesByID: map[string]*pb.Occurrence{},
|
||||
notesByID: map[string]*pb.Note{},
|
||||
opsByID: map[string]*opspb.Operation{},
|
||||
projects: map[string]bool{},
|
||||
}
|
||||
}
|
||||
|
||||
// CreateProject adds the specified project to the mem store
|
||||
func (m *memStore) CreateProject(pID string) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if _, ok := m.projects[pID]; ok {
|
||||
return status.Error(codes.AlreadyExists, fmt.Sprintf("Project with name %q already exists", pID))
|
||||
}
|
||||
m.projects[pID] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteProject deletes the project with the given pID from the mem store
|
||||
func (m *memStore) DeleteProject(pID string) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if _, ok := m.projects[pID]; !ok {
|
||||
return status.Error(codes.NotFound, fmt.Sprintf("Project with name %q does not Exist", pID))
|
||||
}
|
||||
delete(m.projects, pID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetProject returns the project with the given pID from the mem store
|
||||
func (m *memStore) GetProject(pID string) (*pb.Project, error) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
if _, ok := m.projects[pID]; !ok {
|
||||
return nil, status.Error(codes.NotFound, fmt.Sprintf("Project with name %q does not Exist", pID))
|
||||
}
|
||||
return &pb.Project{Name: name.FormatProject(pID)}, nil
|
||||
}
|
||||
|
||||
// ListProjects returns the project id for all projects from the mem store
|
||||
func (m *memStore) ListProjects(filters string) []*pb.Project {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
projects := make([]*pb.Project, len(m.projects))
|
||||
i := 0
|
||||
for k := range m.projects {
|
||||
projects[i] = &pb.Project{Name: name.FormatProject(k)}
|
||||
i++
|
||||
}
|
||||
return projects
|
||||
}
|
||||
|
||||
// CreateOccurrence adds the specified occurrence to the mem store
|
||||
func (m *memStore) CreateOccurrence(o *pb.Occurrence) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if _, ok := m.occurrencesByID[o.Name]; ok {
|
||||
return status.Error(codes.AlreadyExists, fmt.Sprintf("Occurrence with name %q already exists", o.Name))
|
||||
}
|
||||
m.occurrencesByID[o.Name] = o
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteOccurrence deletes the occurrence with the given pID and oID from the memStore
|
||||
func (m *memStore) DeleteOccurrence(pID, oID string) error {
|
||||
oName := name.OccurrenceName(pID, oID)
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if _, ok := m.occurrencesByID[oName]; !ok {
|
||||
return status.Error(codes.NotFound, fmt.Sprintf("Occurrence with oName %q does not Exist", oName))
|
||||
}
|
||||
delete(m.occurrencesByID, oName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateOccurrence updates the existing occurrence with the given projectID and occurrenceID
|
||||
func (m *memStore) UpdateOccurrence(pID, oID string, o *pb.Occurrence) error {
|
||||
oName := name.OccurrenceName(pID, oID)
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if _, ok := m.occurrencesByID[oName]; !ok {
|
||||
return status.Error(codes.NotFound, fmt.Sprintf("Occurrence with oName %q does not Exist", oName))
|
||||
}
|
||||
m.occurrencesByID[oName] = o
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOccurrence returns the occurrence with pID and oID
|
||||
func (m *memStore) GetOccurrence(pID, oID string) (*pb.Occurrence, error) {
|
||||
oName := name.OccurrenceName(pID, oID)
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
o, ok := m.occurrencesByID[oName]
|
||||
if !ok {
|
||||
return nil, status.Error(codes.NotFound, fmt.Sprintf("Occurrence with name %q does not Exist", oName))
|
||||
}
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// ListOccurrences returns the occurrences for this project ID (pID)
|
||||
func (m *memStore) ListOccurrences(pID, filters string) []*pb.Occurrence {
|
||||
os := []*pb.Occurrence{}
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
for _, o := range m.occurrencesByID {
|
||||
if strings.HasPrefix(o.Name, fmt.Sprintf("projects/%v", pID)) {
|
||||
os = append(os, o)
|
||||
}
|
||||
}
|
||||
return os
|
||||
}
|
||||
|
||||
// CreateNote adds the specified note to the mem store
|
||||
func (m *memStore) CreateNote(n *pb.Note) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if _, ok := m.notesByID[n.Name]; ok {
|
||||
return status.Error(codes.AlreadyExists, fmt.Sprintf("Note with name %q already exists", n.Name))
|
||||
}
|
||||
m.notesByID[n.Name] = n
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteNote deletes the note with the given pID and nID from the memStore
|
||||
func (m *memStore) DeleteNote(pID, nID string) error {
|
||||
nName := name.NoteName(pID, nID)
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if _, ok := m.notesByID[nName]; !ok {
|
||||
return status.Error(codes.NotFound, fmt.Sprintf("Note with name %q does not Exist", nName))
|
||||
}
|
||||
delete(m.notesByID, nName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateNote updates the existing note with the given pID and nID
|
||||
func (m *memStore) UpdateNote(pID, nID string, n *pb.Note) error {
|
||||
nName := name.NoteName(pID, nID)
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if _, ok := m.notesByID[nName]; !ok {
|
||||
return status.Error(codes.NotFound, fmt.Sprintf("Note with name %q does not Exist", nName))
|
||||
}
|
||||
m.notesByID[nName] = n
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNote returns the note with pID and nID
|
||||
func (m *memStore) GetNote(pID, nID string) (*pb.Note, error) {
|
||||
nName := name.NoteName(pID, nID)
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
n, ok := m.notesByID[nName]
|
||||
if !ok {
|
||||
return nil, status.Error(codes.NotFound, fmt.Sprintf("Note with name %q does not Exist", nName))
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// GetNoteByOccurrence returns the note attached to occurrence with pID and oID
|
||||
func (m *memStore) GetNoteByOccurrence(pID, oID string) (*pb.Note, error) {
|
||||
oName := name.OccurrenceName(pID, oID)
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
o, ok := m.occurrencesByID[oName]
|
||||
if !ok {
|
||||
return nil, status.Error(codes.NotFound, fmt.Sprintf("Occurrence with name %q does not Exist", oName))
|
||||
}
|
||||
n, ok := m.notesByID[o.NoteName]
|
||||
if !ok {
|
||||
return nil, status.Error(codes.NotFound, fmt.Sprintf("Note with name %q does not Exist", o.NoteName))
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// ListNotes returns the notes for for this project (pID)
|
||||
func (m *memStore) ListNotes(pID, filters string) []*pb.Note {
|
||||
ns := []*pb.Note{}
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
for _, n := range m.notesByID {
|
||||
if strings.HasPrefix(n.Name, fmt.Sprintf("projects/%v", pID)) {
|
||||
ns = append(ns, n)
|
||||
}
|
||||
}
|
||||
return ns
|
||||
}
|
||||
|
||||
// ListNoteOccurrences returns the occcurrences on the particular note (nID) for this project (pID)
|
||||
func (m *memStore) ListNoteOccurrences(pID, nID, filters string) ([]*pb.Occurrence, error) {
|
||||
// TODO: use filters
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
// Verify that note exists
|
||||
if _, err := m.GetNote(pID, nID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nName := name.FormatNote(pID, nID)
|
||||
os := []*pb.Occurrence{}
|
||||
for _, o := range m.occurrencesByID {
|
||||
if o.NoteName == nName {
|
||||
os = append(os, o)
|
||||
}
|
||||
}
|
||||
return os, nil
|
||||
}
|
||||
|
||||
// GetOperation returns the operation with pID and oID
|
||||
func (m *memStore) GetOperation(pID, opID string) (*opspb.Operation, error) {
|
||||
oName := name.OperationName(pID, opID)
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
o, ok := m.opsByID[oName]
|
||||
if !ok {
|
||||
return nil, status.Error(codes.NotFound, fmt.Sprintf("Operation with name %q does not Exist", oName))
|
||||
}
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// CreateOperation adds the specified operation to the mem store
|
||||
func (m *memStore) CreateOperation(o *opspb.Operation) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if _, ok := m.opsByID[o.Name]; ok {
|
||||
return status.Error(codes.AlreadyExists, fmt.Sprintf("Operation with name %q already exists", o.Name))
|
||||
}
|
||||
m.opsByID[o.Name] = o
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteOperation deletes the operation with the given pID and oID from the memStore
|
||||
func (m *memStore) DeleteOperation(pID, opID string) error {
|
||||
opName := name.OperationName(pID, opID)
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if _, ok := m.opsByID[opName]; !ok {
|
||||
return status.Error(codes.NotFound, fmt.Sprintf("Operation with name %q does not Exist", opName))
|
||||
}
|
||||
delete(m.occurrencesByID, opName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateOperation updates the existing operation with the given pID and nID
|
||||
func (m *memStore) UpdateOperation(pID, opID string, op *opspb.Operation) error {
|
||||
opName := name.OperationName(pID, opID)
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if _, ok := m.opsByID[opName]; !ok {
|
||||
return status.Error(codes.NotFound, fmt.Sprintf("Operation with name %q does not Exist", opName))
|
||||
}
|
||||
m.opsByID[opName] = op
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListOperations returns the operations for this project (pID)
|
||||
func (m *memStore) ListOperations(pID, filters string) []*opspb.Operation {
|
||||
ops := []*opspb.Operation{}
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
for _, op := range m.opsByID {
|
||||
if strings.HasPrefix(op.Name, fmt.Sprintf("projects/%v", pID)) {
|
||||
ops = append(ops, op)
|
||||
}
|
||||
}
|
||||
return ops
|
||||
}
|
568
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/storage/memstore_test.go
generated
vendored
Normal file
568
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/storage/memstore_test.go
generated
vendored
Normal file
@ -0,0 +1,568 @@
|
||||
// Copyright 2017 The Grafeas 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES 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 (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/grafeas/grafeas/samples/server/go-server/api/server/name"
|
||||
"github.com/grafeas/grafeas/samples/server/go-server/api/server/testing"
|
||||
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
pb "github.com/grafeas/grafeas/v1alpha1/proto"
|
||||
opspb "google.golang.org/genproto/googleapis/longrunning"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func TestCreateProject(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
p := "myproject"
|
||||
if err := s.CreateProject(p); err != nil {
|
||||
t.Errorf("CreateProject got %v want success", err)
|
||||
}
|
||||
// Try to insert the same project twice, expect failure.
|
||||
if err := s.CreateProject(p); err == nil {
|
||||
t.Errorf("CreateProject got success, want Error")
|
||||
} else if s, _ := status.FromError(err); s.Code() != codes.AlreadyExists {
|
||||
t.Errorf("CreateProject got code %v want %v", s.Code(), codes.AlreadyExists)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateNote(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
nPID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(nPID)
|
||||
if err := s.CreateNote(n); err != nil {
|
||||
t.Errorf("CreateNote got %v want success", err)
|
||||
}
|
||||
// Try to insert the same note twice, expect failure.
|
||||
if err := s.CreateNote(n); err == nil {
|
||||
t.Errorf("CreateNote got success, want Error")
|
||||
} else if s, _ := status.FromError(err); s.Code() != codes.AlreadyExists {
|
||||
t.Errorf("CreateNote got code %v want %v", s.Code(), codes.AlreadyExists)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateOccurrence(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
nPID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(nPID)
|
||||
if err := s.CreateNote(n); err != nil {
|
||||
t.Fatalf("CreateNote got %v want success", err)
|
||||
}
|
||||
oPID := "occurrence-project"
|
||||
o := testutil.Occurrence(oPID, n.Name)
|
||||
if err := s.CreateOccurrence(o); err != nil {
|
||||
t.Errorf("CreateOccurrence got %v want success", err)
|
||||
}
|
||||
// Try to insert the same occurrence twice, expect failure.
|
||||
if err := s.CreateOccurrence(o); err == nil {
|
||||
t.Errorf("CreateOccurrence got success, want Error")
|
||||
} else if s, _ := status.FromError(err); s.Code() != codes.AlreadyExists {
|
||||
t.Errorf("CreateOccurrence got code %v want %v", s.Code(), codes.AlreadyExists)
|
||||
}
|
||||
pID, oID, err := name.ParseOccurrence(o.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing projectID and occurrenceID %v", err)
|
||||
}
|
||||
if got, err := s.GetOccurrence(pID, oID); err != nil {
|
||||
t.Fatalf("GetOccurrence got %v, want success", err)
|
||||
} else if !reflect.DeepEqual(got, o) {
|
||||
t.Errorf("GetOccurrence got %v, want %v", got, o)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateOperation(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
opPID := "vulnerability-scanner-a"
|
||||
op := testutil.Operation(opPID)
|
||||
if err := s.CreateOperation(op); err != nil {
|
||||
t.Errorf("CreateOperation got %v want success", err)
|
||||
}
|
||||
// Try to insert the same note twice, expect failure.
|
||||
if err := s.CreateOperation(op); err == nil {
|
||||
t.Errorf("CreateOperation got success, want Error")
|
||||
} else if s, _ := status.FromError(err); s.Code() != codes.AlreadyExists {
|
||||
t.Errorf("CreateOperation got code %v want %v", s.Code(), codes.AlreadyExists)
|
||||
}
|
||||
}
|
||||
func TestDeleteProject(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
pID := "myproject"
|
||||
// Delete before the note exists
|
||||
if err := s.DeleteProject(pID); err == nil {
|
||||
t.Error("Deleting nonexistant note got success, want error")
|
||||
}
|
||||
if err := s.CreateProject(pID); err != nil {
|
||||
t.Fatalf("CreateProject got %v want success", err)
|
||||
}
|
||||
|
||||
if err := s.DeleteProject(pID); err != nil {
|
||||
t.Errorf("DeleteProject got %v, want success ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteOccurrence(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
nPID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(nPID)
|
||||
if err := s.CreateNote(n); err != nil {
|
||||
t.Fatalf("CreateNote got %v want success", err)
|
||||
}
|
||||
oPID := "occurrence-project"
|
||||
o := testutil.Occurrence(oPID, n.Name)
|
||||
// Delete before the occurrence exists
|
||||
pID, oID, err := name.ParseOccurrence(o.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing occurrence %v", err)
|
||||
}
|
||||
if err := s.DeleteOccurrence(pID, oID); err == nil {
|
||||
t.Error("Deleting nonexistant occurrence got success, want error")
|
||||
}
|
||||
if err := s.CreateOccurrence(o); err != nil {
|
||||
t.Fatalf("CreateOccurrence got %v want success", err)
|
||||
}
|
||||
if err := s.DeleteOccurrence(pID, oID); err != nil {
|
||||
t.Errorf("DeleteOccurrence got %v, want success ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateOccurrence(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
nPID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(nPID)
|
||||
if err := s.CreateNote(n); err != nil {
|
||||
t.Fatalf("CreateNote got %v want success", err)
|
||||
}
|
||||
oPID := "occurrence-project"
|
||||
o := testutil.Occurrence(oPID, n.Name)
|
||||
pID, oID, err := name.ParseOccurrence(o.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing projectID and occurrenceID %v", err)
|
||||
}
|
||||
if err := s.UpdateOccurrence(pID, oID, o); err == nil {
|
||||
t.Fatal("UpdateOccurrence got success want error")
|
||||
}
|
||||
if err := s.CreateOccurrence(o); err != nil {
|
||||
t.Fatalf("CreateOccurrence got %v want success", err)
|
||||
}
|
||||
if got, err := s.GetOccurrence(pID, oID); err != nil {
|
||||
t.Fatalf("GetOccurrence got %v, want success", err)
|
||||
} else if !reflect.DeepEqual(got, o) {
|
||||
t.Errorf("GetOccurrence got %v, want %v", got, o)
|
||||
}
|
||||
|
||||
o2 := o
|
||||
o2.GetVulnerabilityDetails().CvssScore = 1.0
|
||||
if err := s.UpdateOccurrence(pID, oID, o2); err != nil {
|
||||
t.Fatalf("UpdateOccurrence got %v want success", err)
|
||||
}
|
||||
|
||||
if got, err := s.GetOccurrence(pID, oID); err != nil {
|
||||
t.Fatalf("GetOccurrence got %v, want success", err)
|
||||
} else if !reflect.DeepEqual(got, o2) {
|
||||
t.Errorf("GetOccurrence got %v, want %v", got, o2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteNote(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
nPID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(nPID)
|
||||
// Delete before the note exists
|
||||
pID, oID, err := name.ParseNote(n.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing note %v", err)
|
||||
}
|
||||
if err := s.DeleteNote(pID, oID); err == nil {
|
||||
t.Error("Deleting nonexistant note got success, want error")
|
||||
}
|
||||
if err := s.CreateNote(n); err != nil {
|
||||
t.Fatalf("CreateNote got %v want success", err)
|
||||
}
|
||||
|
||||
if err := s.DeleteNote(pID, oID); err != nil {
|
||||
t.Errorf("DeleteNote got %v, want success ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateNote(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
nPID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(nPID)
|
||||
|
||||
pID, nID, err := name.ParseNote(n.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing projectID and noteID %v", err)
|
||||
}
|
||||
if err := s.UpdateNote(pID, nID, n); err == nil {
|
||||
t.Fatal("UpdateNote got success want error")
|
||||
}
|
||||
if err := s.CreateNote(n); err != nil {
|
||||
t.Fatalf("CreateNote got %v want success", err)
|
||||
}
|
||||
if got, err := s.GetNote(pID, nID); err != nil {
|
||||
t.Fatalf("GetNote got %v, want success", err)
|
||||
} else if !reflect.DeepEqual(got, n) {
|
||||
t.Errorf("GetNote got %v, want %v", got, n)
|
||||
}
|
||||
|
||||
n2 := n
|
||||
n2.GetVulnerabilityType().CvssScore = 1.0
|
||||
if err := s.UpdateNote(pID, nID, n2); err != nil {
|
||||
t.Fatalf("UpdateNote got %v want success", err)
|
||||
}
|
||||
|
||||
if got, err := s.GetNote(pID, nID); err != nil {
|
||||
t.Fatalf("GetNote got %v, want success", err)
|
||||
} else if !reflect.DeepEqual(got, n2) {
|
||||
t.Errorf("GetNote got %v, want %v", got, n2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetProject(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
pID := "myproject"
|
||||
// Try to get project before it has been created, expect failure.
|
||||
if _, err := s.GetProject(pID); err == nil {
|
||||
t.Errorf("GetProject got success, want Error")
|
||||
} else if s, _ := status.FromError(err); s.Code() != codes.NotFound {
|
||||
t.Errorf("GetProject got code %v want %v", s.Code(), codes.NotFound)
|
||||
}
|
||||
s.CreateProject(pID)
|
||||
if p, err := s.GetProject(pID); err != nil {
|
||||
t.Fatalf("GetProject got %v want success", err)
|
||||
} else if p.Name != name.FormatProject(pID) {
|
||||
t.Fatalf("Got %s want %s", p.Name, pID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOccurrence(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
nPID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(nPID)
|
||||
if err := s.CreateNote(n); err != nil {
|
||||
t.Fatalf("CreateNote got %v want success", err)
|
||||
}
|
||||
oPID := "occurrence-project"
|
||||
o := testutil.Occurrence(oPID, n.Name)
|
||||
pID, oID, err := name.ParseOccurrence(o.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing occurrence %v", err)
|
||||
}
|
||||
if _, err := s.GetOccurrence(pID, oID); err == nil {
|
||||
t.Fatal("GetOccurrence got success, want error")
|
||||
}
|
||||
if err := s.CreateOccurrence(o); err != nil {
|
||||
t.Errorf("CreateOccurrence got %v, want Success", err)
|
||||
}
|
||||
if got, err := s.GetOccurrence(pID, oID); err != nil {
|
||||
t.Fatalf("GetOccurrence got %v, want success", err)
|
||||
} else if !reflect.DeepEqual(got, o) {
|
||||
t.Errorf("GetOccurrence got %v, want %v", got, o)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNote(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
nPID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(nPID)
|
||||
|
||||
pID, nID, err := name.ParseNote(n.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing note %v", err)
|
||||
}
|
||||
if _, err := s.GetNote(pID, nID); err == nil {
|
||||
t.Fatal("GetNote got success, want error")
|
||||
}
|
||||
if err := s.CreateNote(n); err != nil {
|
||||
t.Errorf("CreateNote got %v, want Success", err)
|
||||
}
|
||||
if got, err := s.GetNote(pID, nID); err != nil {
|
||||
t.Fatalf("GetNote got %v, want success", err)
|
||||
} else if !reflect.DeepEqual(got, n) {
|
||||
t.Errorf("GetNote got %v, want %v", got, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNoteByOccurrence(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
nPID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(nPID)
|
||||
if err := s.CreateNote(n); err != nil {
|
||||
t.Fatalf("CreateNote got %v want success", err)
|
||||
}
|
||||
oPID := "occurrence-project"
|
||||
o := testutil.Occurrence(oPID, n.Name)
|
||||
pID, oID, err := name.ParseOccurrence(o.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing occurrence %v", err)
|
||||
}
|
||||
if _, err := s.GetNoteByOccurrence(pID, oID); err == nil {
|
||||
t.Fatal("GetNoteByOccurrence got success, want error")
|
||||
}
|
||||
if err := s.CreateOccurrence(o); err != nil {
|
||||
t.Errorf("CreateOccurrence got %v, want Success", err)
|
||||
}
|
||||
if got, err := s.GetNoteByOccurrence(pID, oID); err != nil {
|
||||
t.Fatalf("GetNoteByOccurrence got %v, want success", err)
|
||||
} else if !reflect.DeepEqual(got, n) {
|
||||
t.Errorf("GetNoteByOccurrence got %v, want %v", got, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOperation(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
oPID := "vulnerability-scanner-a"
|
||||
o := testutil.Operation(oPID)
|
||||
|
||||
pID, oID, err := name.ParseOperation(o.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing operation %v", err)
|
||||
}
|
||||
if _, err := s.GetOperation(pID, oID); err == nil {
|
||||
t.Fatal("GetOperation got success, want error")
|
||||
}
|
||||
if err := s.CreateOperation(o); err != nil {
|
||||
t.Errorf("CreateOperation got %v, want Success", err)
|
||||
}
|
||||
if got, err := s.GetOperation(pID, oID); err != nil {
|
||||
t.Fatalf("GetOperation got %v, want success", err)
|
||||
} else if !reflect.DeepEqual(got, o) {
|
||||
t.Errorf("GetOperation got %v, want %v", got, o)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteOperation(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
oPID := "vulnerability-scanner-a"
|
||||
o := testutil.Operation(oPID)
|
||||
// Delete before the operation exists
|
||||
pID, oID, err := name.ParseOperation(o.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing note %v", err)
|
||||
}
|
||||
if err := s.DeleteOperation(pID, oID); err == nil {
|
||||
t.Error("Deleting nonexistant operation got success, want error")
|
||||
}
|
||||
if err := s.CreateOperation(o); err != nil {
|
||||
t.Fatalf("CreateOperation got %v want success", err)
|
||||
}
|
||||
|
||||
if err := s.DeleteOperation(pID, oID); err != nil {
|
||||
t.Errorf("DeleteOperation got %v, want success ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateOperation(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
oPID := "vulnerability-scanner-a"
|
||||
o := testutil.Operation(oPID)
|
||||
|
||||
pID, oID, err := name.ParseOperation(o.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing projectID and operationID %v", err)
|
||||
}
|
||||
if err := s.UpdateOperation(pID, oID, o); err == nil {
|
||||
t.Fatal("UpdateOperation got success want error")
|
||||
}
|
||||
if err := s.CreateOperation(o); err != nil {
|
||||
t.Fatalf("CreateOperation got %v want success", err)
|
||||
}
|
||||
if got, err := s.GetOperation(pID, oID); err != nil {
|
||||
t.Fatalf("GetOperation got %v, want success", err)
|
||||
} else if !reflect.DeepEqual(got, o) {
|
||||
t.Errorf("GetOperation got %v, want %v", got, o)
|
||||
}
|
||||
|
||||
o2 := o
|
||||
o2.Done = true
|
||||
if err := s.UpdateOperation(pID, oID, o2); err != nil {
|
||||
t.Fatalf("UpdateOperation got %v want success", err)
|
||||
}
|
||||
|
||||
if got, err := s.GetOperation(pID, oID); err != nil {
|
||||
t.Fatalf("GetOperation got %v, want success", err)
|
||||
} else if !reflect.DeepEqual(got, o2) {
|
||||
t.Errorf("GetOperation got %v, want %v", got, o2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListProjects(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
wantProjectNames := []string{}
|
||||
for i := 0; i < 20; i++ {
|
||||
pID := fmt.Sprint("Project", i)
|
||||
if err := s.CreateProject(pID); err != nil {
|
||||
t.Fatalf("CreateProject got %v want success", err)
|
||||
}
|
||||
wantProjectNames = append(wantProjectNames, name.FormatProject(pID))
|
||||
}
|
||||
filter := "filters_are_yet_to_be_implemented"
|
||||
gotProjects := s.ListProjects(filter)
|
||||
if len(gotProjects) != 20 {
|
||||
t.Errorf("ListProjects got %v operations, want 20", len(gotProjects))
|
||||
}
|
||||
gotProjectNames := make([]string, len(gotProjects))
|
||||
for i, project := range gotProjects {
|
||||
gotProjectNames[i] = project.Name
|
||||
}
|
||||
// Sort to handle that wantProjectNames are not guaranteed to be listed in insertion order
|
||||
sort.Strings(wantProjectNames)
|
||||
sort.Strings(gotProjectNames)
|
||||
if !reflect.DeepEqual(gotProjectNames, wantProjectNames) {
|
||||
t.Errorf("ListProjects got %v want %v", gotProjectNames, wantProjectNames)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListOperations(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
ops := []opspb.Operation{}
|
||||
findProject := "findThese"
|
||||
dontFind := "dontFind"
|
||||
for i := 0; i < 20; i++ {
|
||||
o := testutil.Operation("")
|
||||
if i < 5 {
|
||||
o.Name = name.FormatOperation(findProject, string(i))
|
||||
} else {
|
||||
o.Name = name.FormatOperation(dontFind, string(i))
|
||||
}
|
||||
if err := s.CreateOperation(o); err != nil {
|
||||
t.Fatalf("CreateOperation got %v want success", err)
|
||||
}
|
||||
ops = append(ops, *o)
|
||||
}
|
||||
gotOs := s.ListOperations(findProject, "")
|
||||
|
||||
if len(gotOs) != 5 {
|
||||
t.Errorf("ListOperations got %v operations, want 5", len(gotOs))
|
||||
}
|
||||
for _, o := range gotOs {
|
||||
want := name.FormatProject(findProject)
|
||||
if !strings.HasPrefix(o.Name, want) {
|
||||
t.Errorf("ListOperations got %v want prefix %v", o.Name, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestListNotes(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
ns := []*pb.Note{}
|
||||
findProject := "findThese"
|
||||
dontFind := "dontFind"
|
||||
for i := 0; i < 20; i++ {
|
||||
n := testutil.Note("")
|
||||
if i < 5 {
|
||||
n.Name = name.FormatNote(findProject, string(i))
|
||||
} else {
|
||||
n.Name = name.FormatNote(dontFind, string(i))
|
||||
}
|
||||
if err := s.CreateNote(n); err != nil {
|
||||
t.Fatalf("CreateNote got %v want success", err)
|
||||
}
|
||||
ns = append(ns, n)
|
||||
}
|
||||
gotNs := s.ListNotes(findProject, "")
|
||||
if len(gotNs) != 5 {
|
||||
t.Errorf("ListNotes got %v operations, want 5", len(gotNs))
|
||||
}
|
||||
for _, n := range gotNs {
|
||||
want := name.FormatProject(findProject)
|
||||
if !strings.HasPrefix(n.Name, want) {
|
||||
t.Errorf("ListNotes got %v want %v", n.Name, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestListOccurrences(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
os := []*pb.Occurrence{}
|
||||
findProject := "findThese"
|
||||
dontFind := "dontFind"
|
||||
nPID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(nPID)
|
||||
if err := s.CreateNote(n); err != nil {
|
||||
t.Fatalf("CreateNote got %v want success", err)
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
oPID := "_"
|
||||
o := testutil.Occurrence(oPID, n.Name)
|
||||
if i < 5 {
|
||||
o.Name = name.FormatOccurrence(findProject, string(i))
|
||||
} else {
|
||||
o.Name = name.FormatOccurrence(dontFind, string(i))
|
||||
}
|
||||
if err := s.CreateOccurrence(o); err != nil {
|
||||
t.Fatalf("CreateOccurrence got %v want success", err)
|
||||
}
|
||||
os = append(os, o)
|
||||
}
|
||||
gotOs := s.ListOccurrences(findProject, "")
|
||||
if len(gotOs) != 5 {
|
||||
t.Errorf("ListOccurrences got %v Occurrences, want 5", len(gotOs))
|
||||
}
|
||||
for _, o := range gotOs {
|
||||
want := name.FormatProject(findProject)
|
||||
if !strings.HasPrefix(o.Name, want) {
|
||||
t.Errorf("ListOccurrences got %v want %v", o.Name, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestListNoteOccurrences(t *testing.T) {
|
||||
s := NewMemStore()
|
||||
os := []*pb.Occurrence{}
|
||||
findProject := "findThese"
|
||||
dontFind := "dontFind"
|
||||
nPID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(nPID)
|
||||
if err := s.CreateNote(n); err != nil {
|
||||
t.Fatalf("CreateNote got %v want success", err)
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
oPID := "_"
|
||||
o := testutil.Occurrence(oPID, n.Name)
|
||||
if i < 5 {
|
||||
o.Name = name.FormatOccurrence(findProject, string(i))
|
||||
} else {
|
||||
o.Name = name.FormatOccurrence(dontFind, string(i))
|
||||
}
|
||||
if err := s.CreateOccurrence(o); err != nil {
|
||||
t.Fatalf("CreateOccurrence got %v want success", err)
|
||||
}
|
||||
os = append(os, o)
|
||||
}
|
||||
pID, nID, err := name.ParseNote(n.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing note name %v", err)
|
||||
}
|
||||
gotOs, err := s.ListNoteOccurrences(pID, nID, "")
|
||||
if err != nil {
|
||||
t.Fatalf("ListNoteOccurrences got %v want success", err)
|
||||
}
|
||||
if len(gotOs) != 20 {
|
||||
t.Errorf("ListNoteOccurrences got %v Occurrences, want 20", len(gotOs))
|
||||
}
|
||||
for _, o := range gotOs {
|
||||
if o.NoteName != n.Name {
|
||||
t.Errorf("ListNoteOccurrences got %v want %v", o.Name, o.NoteName)
|
||||
}
|
||||
}
|
||||
}
|
182
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/testing/testobjects.go
generated
vendored
Normal file
182
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/testing/testobjects.go
generated
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
// Copyright 2017 The Grafeas 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/any"
|
||||
pb "github.com/grafeas/grafeas/v1alpha1/proto"
|
||||
opspb "google.golang.org/genproto/googleapis/longrunning"
|
||||
)
|
||||
|
||||
func Occurrence(pID, noteName string) *pb.Occurrence {
|
||||
return &pb.Occurrence{
|
||||
Name: fmt.Sprintf("projects/%s/occurrences/134", pID),
|
||||
ResourceUrl: "gcr.io/foo/bar",
|
||||
NoteName: noteName,
|
||||
Kind: pb.Note_PACKAGE_VULNERABILITY,
|
||||
Details: &pb.Occurrence_VulnerabilityDetails{
|
||||
VulnerabilityDetails: &pb.VulnerabilityType_VulnerabilityDetails{
|
||||
Severity: pb.VulnerabilityType_HIGH,
|
||||
CvssScore: 7.5,
|
||||
PackageIssue: []*pb.VulnerabilityType_PackageIssue{
|
||||
&pb.VulnerabilityType_PackageIssue{
|
||||
SeverityName: "HIGH",
|
||||
AffectedLocation: &pb.VulnerabilityType_VulnerabilityLocation{
|
||||
CpeUri: "cpe:/o:debian:debian_linux:8",
|
||||
Package: "icu",
|
||||
Version: &pb.VulnerabilityType_Version{
|
||||
Name: "52.1",
|
||||
Revision: "8+deb8u3",
|
||||
},
|
||||
},
|
||||
FixedLocation: &pb.VulnerabilityType_VulnerabilityLocation{
|
||||
CpeUri: "cpe:/o:debian:debian_linux:8",
|
||||
Package: "icu",
|
||||
Version: &pb.VulnerabilityType_Version{
|
||||
Name: "52.1",
|
||||
Revision: "8+deb8u4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Note(pID string) *pb.Note {
|
||||
return &pb.Note{
|
||||
Name: fmt.Sprintf("projects/%s/notes/CVE-1999-0710", pID),
|
||||
ShortDescription: "CVE-2014-9911",
|
||||
LongDescription: "NIST vectors: AV:N/AC:L/Au:N/C:P/I:P",
|
||||
Kind: pb.Note_PACKAGE_VULNERABILITY,
|
||||
NoteType: &pb.Note_VulnerabilityType{
|
||||
&pb.VulnerabilityType{
|
||||
CvssScore: 7.5,
|
||||
Severity: pb.VulnerabilityType_HIGH,
|
||||
Details: []*pb.VulnerabilityType_Detail{
|
||||
&pb.VulnerabilityType_Detail{
|
||||
CpeUri: "cpe:/o:debian:debian_linux:7",
|
||||
Package: "icu",
|
||||
Description: "Stack-based buffer overflow in the ures_getByKeyWithFallback function in " +
|
||||
"common/uresbund.cpp in International Components for Unicode (ICU) before 54.1 for C/C++ allows " +
|
||||
"remote attackers to cause a denial of service or possibly have unspecified other impact via a crafted uloc_getDisplayName call.",
|
||||
MinAffectedVersion: &pb.VulnerabilityType_Version{
|
||||
Kind: pb.VulnerabilityType_Version_MINIMUM,
|
||||
},
|
||||
SeverityName: "HIGH",
|
||||
|
||||
FixedLocation: &pb.VulnerabilityType_VulnerabilityLocation{
|
||||
CpeUri: "cpe:/o:debian:debian_linux:7",
|
||||
Package: "icu",
|
||||
Version: &pb.VulnerabilityType_Version{
|
||||
Name: "4.8.1.1",
|
||||
Revision: "12+deb7u6",
|
||||
},
|
||||
},
|
||||
},
|
||||
&pb.VulnerabilityType_Detail{
|
||||
CpeUri: "cpe:/o:debian:debian_linux:8",
|
||||
Package: "icu",
|
||||
Description: "Stack-based buffer overflow in the ures_getByKeyWithFallback function in " +
|
||||
"common/uresbund.cpp in International Components for Unicode (ICU) before 54.1 for C/C++ allows " +
|
||||
"remote attackers to cause a denial of service or possibly have unspecified other impact via a crafted uloc_getDisplayName call.",
|
||||
MinAffectedVersion: &pb.VulnerabilityType_Version{
|
||||
Kind: pb.VulnerabilityType_Version_MINIMUM,
|
||||
},
|
||||
SeverityName: "HIGH",
|
||||
|
||||
FixedLocation: &pb.VulnerabilityType_VulnerabilityLocation{
|
||||
CpeUri: "cpe:/o:debian:debian_linux:8",
|
||||
Package: "icu",
|
||||
Version: &pb.VulnerabilityType_Version{
|
||||
Name: "52.1",
|
||||
Revision: "8+deb8u4",
|
||||
},
|
||||
},
|
||||
},
|
||||
&pb.VulnerabilityType_Detail{
|
||||
CpeUri: "cpe:/o:debian:debian_linux:9",
|
||||
Package: "icu",
|
||||
Description: "Stack-based buffer overflow in the ures_getByKeyWithFallback function in " +
|
||||
"common/uresbund.cpp in International Components for Unicode (ICU) before 54.1 for C/C++ allows " +
|
||||
"remote attackers to cause a denial of service or possibly have unspecified other impact via a crafted uloc_getDisplayName call.",
|
||||
MinAffectedVersion: &pb.VulnerabilityType_Version{
|
||||
Kind: pb.VulnerabilityType_Version_MINIMUM,
|
||||
},
|
||||
SeverityName: "HIGH",
|
||||
|
||||
FixedLocation: &pb.VulnerabilityType_VulnerabilityLocation{
|
||||
CpeUri: "cpe:/o:debian:debian_linux:9",
|
||||
Package: "icu",
|
||||
Version: &pb.VulnerabilityType_Version{
|
||||
Name: "55.1",
|
||||
Revision: "3",
|
||||
},
|
||||
},
|
||||
},
|
||||
&pb.VulnerabilityType_Detail{
|
||||
CpeUri: "cpe:/o:canonical:ubuntu_linux:14.04",
|
||||
Package: "andriod",
|
||||
Description: "Stack-based buffer overflow in the ures_getByKeyWithFallback function in " +
|
||||
"common/uresbund.cpp in International Components for Unicode (ICU) before 54.1 for C/C++ allows " +
|
||||
"remote attackers to cause a denial of service or possibly have unspecified other impact via a crafted uloc_getDisplayName call.",
|
||||
MinAffectedVersion: &pb.VulnerabilityType_Version{
|
||||
Kind: pb.VulnerabilityType_Version_MINIMUM,
|
||||
},
|
||||
SeverityName: "MEDIUM",
|
||||
|
||||
FixedLocation: &pb.VulnerabilityType_VulnerabilityLocation{
|
||||
CpeUri: "cpe:/o:canonical:ubuntu_linux:14.04",
|
||||
Package: "andriod",
|
||||
Version: &pb.VulnerabilityType_Version{
|
||||
Kind: pb.VulnerabilityType_Version_MAXIMUM,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RelatedUrl: []*pb.Note_RelatedUrl{
|
||||
&pb.Note_RelatedUrl{
|
||||
Url: "https://security-tracker.debian.org/tracker/CVE-2014-9911",
|
||||
Label: "More Info",
|
||||
},
|
||||
&pb.Note_RelatedUrl{
|
||||
Url: "http://people.ubuntu.com/~ubuntu-security/cve/CVE-2014-9911",
|
||||
Label: "More Info",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Operation(pID string) *opspb.Operation {
|
||||
md := &pb.OperationMetadata{CreateTime: ptypes.TimestampNow()}
|
||||
bytes, err := proto.Marshal(md)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing bytes: %v", err)
|
||||
return nil
|
||||
}
|
||||
return &opspb.Operation{
|
||||
Name: fmt.Sprintf("projects/%s/operations/foo", pID),
|
||||
Metadata: &any.Any{Value: bytes},
|
||||
Done: false,
|
||||
}
|
||||
}
|
373
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/v1alpha1/impl.go
generated
vendored
Normal file
373
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/v1alpha1/impl.go
generated
vendored
Normal file
@ -0,0 +1,373 @@
|
||||
// Copyright 2017 The Grafeas 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES 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 is an implementation of the v1alpha1 version of Grafeas.
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/grafeas/grafeas/samples/server/go-server/api/server/name"
|
||||
server "github.com/grafeas/grafeas/server-go"
|
||||
pb "github.com/grafeas/grafeas/v1alpha1/proto"
|
||||
"golang.org/x/net/context"
|
||||
opspb "google.golang.org/genproto/googleapis/longrunning"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// Grafeas is an implementation of the Grafeas API, which should be called by handler methods for verification of logic
|
||||
// and storage.
|
||||
type Grafeas struct {
|
||||
S server.Storager
|
||||
}
|
||||
|
||||
// CreateProject validates that a project is valid and then creates a project in the backing datastore.
|
||||
func (g *Grafeas) CreateProject(ctx context.Context, req *pb.CreateProjectRequest) (*empty.Empty, error) {
|
||||
pID, err := name.ParseProject(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing project name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid Project name")
|
||||
}
|
||||
return &empty.Empty{}, g.S.CreateProject(pID)
|
||||
}
|
||||
|
||||
// CreateNote validates that a note is valid and then creates a note in the backing datastore.
|
||||
func (g *Grafeas) CreateNote(ctx context.Context, req *pb.CreateNoteRequest) (*pb.Note, error) {
|
||||
n := req.Note
|
||||
if req == nil {
|
||||
log.Print("Note must not be empty.")
|
||||
return nil, status.Error(codes.InvalidArgument, "Note must not be empty")
|
||||
}
|
||||
if n.Name == "" {
|
||||
log.Printf("Note name must not be empty: %v", n.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Note name must not be empty")
|
||||
}
|
||||
pID, _, err := name.ParseNote(n.Name)
|
||||
if err != nil {
|
||||
log.Printf("Invalid note name: %v", n.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid note name")
|
||||
}
|
||||
if _, err = g.S.GetProject(pID); err != nil {
|
||||
log.Printf("Unable to get project %v, err: %v", pID, err)
|
||||
return nil, status.Error(codes.NotFound, fmt.Sprintf("Project %v not found", pID))
|
||||
}
|
||||
|
||||
// TODO: Validate that operation exists if it is specified when get methods are implmented
|
||||
return n, g.S.CreateNote(n)
|
||||
}
|
||||
|
||||
// CreateOccurrence validates that a note is valid and then creates an occurrence in the backing datastore.
|
||||
func (g *Grafeas) CreateOccurrence(ctx context.Context, req *pb.CreateOccurrenceRequest) (*pb.Occurrence, error) {
|
||||
o := req.Occurrence
|
||||
if req == nil {
|
||||
log.Print("Occurrence must not be empty.")
|
||||
return nil, status.Error(codes.InvalidArgument, "Occurrence must not be empty")
|
||||
}
|
||||
if o.Name == "" {
|
||||
log.Printf("Invalid occurrence name: %v", o.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid occurrence name")
|
||||
}
|
||||
if o.NoteName == "" {
|
||||
log.Print("No note is associated with this occurrence")
|
||||
}
|
||||
pID, _, err := name.ParseOccurrence(o.Name)
|
||||
if _, err = g.S.GetProject(pID); err != nil {
|
||||
log.Printf("Unable to get project %v, err: %v", pID, err)
|
||||
return nil, status.Error(codes.NotFound, fmt.Sprintf("Project %v not found", pID))
|
||||
}
|
||||
pID, nID, err := name.ParseNote(o.NoteName)
|
||||
if err != nil {
|
||||
log.Printf("Invalid note name: %v", o.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid note name")
|
||||
}
|
||||
if n, err := g.S.GetNote(pID, nID); n == nil || err != nil {
|
||||
log.Printf("Unable to getnote %v, err: %v", n, err)
|
||||
return nil, status.Error(codes.NotFound, fmt.Sprintf("Note %v not found", o.NoteName))
|
||||
}
|
||||
// TODO: Validate that operation exists if it is specified
|
||||
return o, g.S.CreateOccurrence(o)
|
||||
}
|
||||
|
||||
// CreateOperation validates that a note is valid and then creates an operation note in the backing datastore.
|
||||
func (g *Grafeas) CreateOperation(ctx context.Context, req *pb.CreateOperationRequest) (*opspb.Operation, error) {
|
||||
o := req.Operation
|
||||
if o.Name == "" {
|
||||
log.Printf("Invalid operation name: %v", o.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid operation name")
|
||||
}
|
||||
pID, _, err := name.ParseOperation(o.Name)
|
||||
if _, err = g.S.GetProject(pID); err != nil {
|
||||
log.Printf("Unable to get project %v, err: %v", pID, err)
|
||||
return nil, status.Error(codes.NotFound, fmt.Sprintf("Project %v not found", pID))
|
||||
}
|
||||
return o, g.S.CreateOperation(o)
|
||||
}
|
||||
|
||||
// DeleteProject deletes a project from the datastore.
|
||||
func (g *Grafeas) DeleteProject(ctx context.Context, req *pb.DeleteProjectRequest) (*empty.Empty, error) {
|
||||
pID, err := name.ParseProject(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing project name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid Project name")
|
||||
}
|
||||
return &empty.Empty{}, g.S.DeleteProject(pID)
|
||||
}
|
||||
|
||||
// DeleteOccurrence deletes an occurrence from the datastore.
|
||||
func (g *Grafeas) DeleteOccurrence(ctx context.Context, req *pb.DeleteOccurrenceRequest) (*empty.Empty, error) {
|
||||
pID, oID, err := name.ParseOccurrence(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid occurrence name")
|
||||
}
|
||||
return &empty.Empty{}, g.S.DeleteOccurrence(pID, oID)
|
||||
}
|
||||
|
||||
// DeleteNote deletes a note from the datastore.
|
||||
func (g *Grafeas) DeleteNote(ctx context.Context, req *pb.DeleteNoteRequest) (*empty.Empty, error) {
|
||||
pID, nID, err := name.ParseNote(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid note name")
|
||||
}
|
||||
// TODO: Check for occurrences tied to this note, and return an error if there are any before deletion.
|
||||
return &empty.Empty{}, g.S.DeleteNote(pID, nID)
|
||||
}
|
||||
|
||||
// DeleteOperation deletes an operation from the datastore.
|
||||
func (g *Grafeas) DeleteOperation(ctx context.Context, req *opspb.DeleteOperationRequest) (*empty.Empty, error) {
|
||||
pID, oID, err := name.ParseOperation(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid Operation name")
|
||||
}
|
||||
// TODO: Check for occurrences and notes tied to this operation, and return an error if there are any before deletion.
|
||||
return &empty.Empty{}, g.S.DeleteOperation(pID, oID)
|
||||
}
|
||||
|
||||
// GetProject gets a project from the datastore.
|
||||
func (g *Grafeas) GetProject(ctx context.Context, req *pb.GetProjectRequest) (*pb.Project, error) {
|
||||
pID, err := name.ParseProject(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing project name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid Project name")
|
||||
}
|
||||
return g.S.GetProject(pID)
|
||||
}
|
||||
|
||||
// GetNote gets a note from the datastore.
|
||||
func (g *Grafeas) GetNote(ctx context.Context, req *pb.GetNoteRequest) (*pb.Note, error) {
|
||||
pID, nID, err := name.ParseNote(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid Note name")
|
||||
}
|
||||
return g.S.GetNote(pID, nID)
|
||||
}
|
||||
|
||||
// GetOccurrence gets a occurrence from the datastore.
|
||||
func (g *Grafeas) GetOccurrence(ctx context.Context, req *pb.GetOccurrenceRequest) (*pb.Occurrence, error) {
|
||||
pID, oID, err := name.ParseOccurrence(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Could note parse name %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Could note parse name")
|
||||
}
|
||||
return g.S.GetOccurrence(pID, oID)
|
||||
}
|
||||
|
||||
// GetOperation gets a occurrence from the datastore.
|
||||
func (g *Grafeas) GetOperation(ctx context.Context, req *opspb.GetOperationRequest) (*opspb.Operation, error) {
|
||||
pID, oID, err := name.ParseOperation(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid Operation name")
|
||||
}
|
||||
return g.S.GetOperation(pID, oID)
|
||||
}
|
||||
|
||||
// GetOccurrenceNote gets a the note for the provided occurrence from the datastore.
|
||||
func (g *Grafeas) GetOccurrenceNote(ctx context.Context, req *pb.GetOccurrenceNoteRequest) (*pb.Note, error) {
|
||||
pID, oID, err := name.ParseOccurrence(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid occurrence name")
|
||||
}
|
||||
o, gErr := g.S.GetOccurrence(pID, oID)
|
||||
if gErr != nil {
|
||||
return nil, gErr
|
||||
}
|
||||
npID, nID, err := name.ParseNote(o.NoteName)
|
||||
if err != nil {
|
||||
log.Printf("Invalid note name: %v", o.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Invalid note name: %v", o.NoteName))
|
||||
}
|
||||
return g.S.GetNote(npID, nID)
|
||||
}
|
||||
|
||||
func (g *Grafeas) UpdateNote(ctx context.Context, req *pb.UpdateNoteRequest) (*pb.Note, error) {
|
||||
pID, nID, err := name.ParseNote(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid Note name")
|
||||
}
|
||||
// get existing note
|
||||
existing, gErr := g.S.GetNote(pID, nID)
|
||||
if gErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
// verify that name didnt change
|
||||
if req.Note.Name != existing.Name {
|
||||
log.Printf("Cannot change note name: %v", req.Note.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Cannot change note name: %v", req.Note.Name))
|
||||
}
|
||||
|
||||
// update note
|
||||
if gErr = g.S.UpdateNote(pID, nID, req.Note); err != nil {
|
||||
log.Printf("Cannot update note : %v", gErr)
|
||||
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Cannot change note name: %v", req.Note.Name))
|
||||
}
|
||||
return g.S.GetNote(pID, nID)
|
||||
}
|
||||
|
||||
func (g *Grafeas) UpdateOccurrence(ctx context.Context, req *pb.UpdateOccurrenceRequest) (*pb.Occurrence, error) {
|
||||
pID, oID, err := name.ParseOccurrence(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid occurrence name")
|
||||
}
|
||||
// get existing Occurrence
|
||||
existing, gErr := g.S.GetOccurrence(pID, oID)
|
||||
if gErr != nil {
|
||||
return nil, gErr
|
||||
}
|
||||
|
||||
// verify that name didnt change
|
||||
if req.Name != existing.Name {
|
||||
log.Printf("Cannot change occurrence name: %v", req.Occurrence.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Cannot change occurrence name: %v", req.Occurrence.Name))
|
||||
}
|
||||
// verify that if note name changed, it still exists
|
||||
if req.Occurrence.NoteName != existing.NoteName {
|
||||
npID, nID, err := name.ParseNote(req.Occurrence.NoteName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if newN, err := g.S.GetNote(npID, nID); newN == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// update Occurrence
|
||||
if gErr = g.S.UpdateOccurrence(pID, oID, req.Occurrence); gErr != nil {
|
||||
log.Printf("Cannot update occurrence : %v", req.Occurrence.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Cannot update Occurrences: %v", err))
|
||||
}
|
||||
return g.S.GetOccurrence(pID, oID)
|
||||
}
|
||||
|
||||
func (g *Grafeas) UpdateOperation(ctx context.Context, req *pb.UpdateOperationRequest) (*opspb.Operation, error) {
|
||||
pID, oID, err := name.ParseOperation(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid Operation name")
|
||||
}
|
||||
// get existing operation
|
||||
existing, gErr := g.S.GetOperation(pID, oID)
|
||||
if gErr != nil {
|
||||
return nil, gErr
|
||||
}
|
||||
|
||||
// verify that operation isn't marked done
|
||||
if req.Operation.Done != existing.Done && existing.Done {
|
||||
log.Printf("Trying to update a done operation")
|
||||
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Cannot update operation in status done: %v", req.Name))
|
||||
}
|
||||
|
||||
// verify that name didnt change
|
||||
if req.Operation.Name != existing.Name {
|
||||
log.Printf("Cannot change operation name: %v", req.Operation.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Cannot change operation name: %v", req.Name))
|
||||
}
|
||||
|
||||
// update operation
|
||||
if gErr = g.S.UpdateOperation(pID, oID, req.Operation); gErr != nil {
|
||||
log.Printf("Cannot update operation : %v", req.Operation.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Cannot update Opreation: %v", req.Operation.Name))
|
||||
}
|
||||
return g.S.GetOperation(pID, oID)
|
||||
}
|
||||
|
||||
// ListProjects returns the project id for all projects in the backing datastore.
|
||||
func (g *Grafeas) ListProjects(ctx context.Context, req *pb.ListProjectsRequest) (*pb.ListProjectsResponse, error) {
|
||||
// TODO: support filters
|
||||
ns := g.S.ListProjects(req.Filter)
|
||||
return &pb.ListProjectsResponse{Projects: ns}, nil
|
||||
}
|
||||
|
||||
func (g *Grafeas) ListOperations(ctx context.Context, req *opspb.ListOperationsRequest) (*opspb.ListOperationsResponse, error) {
|
||||
pID, err := name.ParseProject(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid Project name")
|
||||
}
|
||||
// TODO: support filters
|
||||
ops := g.S.ListOperations(pID, req.Filter)
|
||||
return &opspb.ListOperationsResponse{Operations: ops}, nil
|
||||
}
|
||||
|
||||
func (g *Grafeas) ListNotes(ctx context.Context, req *pb.ListNotesRequest) (*pb.ListNotesResponse, error) {
|
||||
pID, err := name.ParseProject(req.Parent)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing name: %v", req.Parent)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid Project name")
|
||||
}
|
||||
|
||||
// TODO: support filters
|
||||
ns := g.S.ListNotes(pID, req.Filter)
|
||||
return &pb.ListNotesResponse{Notes: ns}, nil
|
||||
|
||||
}
|
||||
|
||||
func (g *Grafeas) ListOccurrences(ctx context.Context, req *pb.ListOccurrencesRequest) (*pb.ListOccurrencesResponse, error) {
|
||||
pID, err := name.ParseProject(req.Parent)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing name: %v", req.Parent)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: support filters - prioritizing resource url
|
||||
os := g.S.ListOccurrences(pID, req.Filter)
|
||||
return &pb.ListOccurrencesResponse{Occurrences: os}, nil
|
||||
}
|
||||
|
||||
func (g *Grafeas) ListNoteOccurrences(ctx context.Context, req *pb.ListNoteOccurrencesRequest) (*pb.ListNoteOccurrencesResponse, error) {
|
||||
pID, nID, err := name.ParseNote(req.Name)
|
||||
if err != nil {
|
||||
log.Printf("Invalid note name: %v", req.Name)
|
||||
return nil, status.Error(codes.InvalidArgument, "Invalid note name")
|
||||
}
|
||||
// TODO: support filters - prioritizing resource url
|
||||
os, gErr := g.S.ListNoteOccurrences(pID, nID, req.Filter)
|
||||
if gErr != nil {
|
||||
return nil, gErr
|
||||
}
|
||||
return &pb.ListNoteOccurrencesResponse{Occurrences: os}, nil
|
||||
}
|
||||
|
||||
func (g *Grafeas) CancelOperation(context.Context, *opspb.CancelOperationRequest) (*empty.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "Currently Unimplemented")
|
||||
}
|
659
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/v1alpha1/impl_test.go
generated
vendored
Normal file
659
vendor/github.com/grafeas/grafeas/samples/server/go-server/api/server/v1alpha1/impl_test.go
generated
vendored
Normal file
@ -0,0 +1,659 @@
|
||||
// Copyright 2017 The Grafeas 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES 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"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/grafeas/grafeas/samples/server/go-server/api/server/name"
|
||||
"github.com/grafeas/grafeas/samples/server/go-server/api/server/storage"
|
||||
"github.com/grafeas/grafeas/samples/server/go-server/api/server/testing"
|
||||
pb "github.com/grafeas/grafeas/v1alpha1/proto"
|
||||
opspb "google.golang.org/genproto/googleapis/longrunning"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func createProject(t *testing.T, pID string, ctx context.Context, g Grafeas) {
|
||||
req := pb.CreateProjectRequest{Name: name.FormatProject(pID)}
|
||||
if _, err := g.CreateProject(ctx, &req); err != nil {
|
||||
t.Errorf("CreateProject(empty operation): got %v, want success", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateProject(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
pID := "myproject"
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
req := pb.CreateProjectRequest{Name: name.FormatProject(pID)}
|
||||
_, err := g.CreateProject(ctx, &req)
|
||||
if err != nil {
|
||||
t.Errorf("CreateProject(empty operation): got %v, want success", err)
|
||||
}
|
||||
_, err = g.CreateProject(ctx, &req)
|
||||
if s, _ := status.FromError(err); s.Code() != codes.AlreadyExists {
|
||||
t.Errorf("CreateProject(empty operation): got %v, want AlreadyExists", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateOperation(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
op := &opspb.Operation{}
|
||||
req := pb.CreateOperationRequest{Parent: "projects/opp", Operation: op}
|
||||
if _, err := g.CreateOperation(ctx, &req); err == nil {
|
||||
t.Error("CreateOperation(empty operation): got success, want error")
|
||||
} else if s, _ := status.FromError(err); s.Code() != codes.InvalidArgument {
|
||||
t.Errorf("CreateOperation(empty operation): got %v, want InvalidArgument", err)
|
||||
}
|
||||
pID := "vulnerability-scanner-a"
|
||||
op = testutil.Operation(pID)
|
||||
parent := name.FormatProject(pID)
|
||||
req = pb.CreateOperationRequest{Parent: parent, Operation: op}
|
||||
if _, err := g.CreateOperation(ctx, &req); err == nil {
|
||||
t.Error("CreateOperation: got success, want error")
|
||||
} else if s, _ := status.FromError(err); s.Code() != codes.NotFound {
|
||||
t.Errorf("CreateOperation: got %v, want NotFound)", err)
|
||||
}
|
||||
createProject(t, pID, ctx, g)
|
||||
if _, err := g.CreateOperation(ctx, &req); error(err) != nil {
|
||||
t.Errorf("CreateOperation(%v) got %#v, want success", op, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateOccurrence(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
pID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(pID)
|
||||
parent := name.FormatProject(pID)
|
||||
createProject(t, pID, ctx, g)
|
||||
req := &pb.CreateNoteRequest{Parent: parent, Note: n}
|
||||
if _, err := g.CreateNote(ctx, req); err != nil {
|
||||
t.Fatalf("CreateNote(%v) got %v, want success", req, err)
|
||||
}
|
||||
oReq := &pb.CreateOccurrenceRequest{Parent: parent, Occurrence: &pb.Occurrence{}}
|
||||
if _, err := g.CreateOccurrence(ctx, oReq); err == nil {
|
||||
t.Error("CreateOccurrence(empty occ): got success, want error")
|
||||
} else if s, _ := status.FromError(err); s.Code() != codes.InvalidArgument {
|
||||
t.Errorf("CreateOccurrence(empty occ): got %v, want InvalidArgument)", err)
|
||||
}
|
||||
pID = "occurrence-project"
|
||||
o := testutil.Occurrence(pID, n.Name)
|
||||
parent = name.FormatProject(pID)
|
||||
oReq = &pb.CreateOccurrenceRequest{Parent: parent, Occurrence: o}
|
||||
// Try to insert an occurrence without first creating its project, expect failure
|
||||
if _, err := g.CreateOccurrence(ctx, oReq); err == nil {
|
||||
t.Error("CreateOccurrence: got success, want error")
|
||||
} else if s, _ := status.FromError(err); s.Code() != codes.NotFound {
|
||||
t.Errorf("CreateOccurrence: got %v, want NotFound)", err)
|
||||
}
|
||||
createProject(t, pID, ctx, g)
|
||||
if _, err := g.CreateOccurrence(ctx, oReq); err != nil {
|
||||
t.Errorf("CreateOccurrence(%v) got %v, want success", oReq, err)
|
||||
}
|
||||
// Try to insert an occurrence for a note that does not exist.
|
||||
o.Name = "projects/testproject/occurrences/nonote"
|
||||
o.NoteName = "projects/scan-provider/notes/notthere"
|
||||
oReq = &pb.CreateOccurrenceRequest{Parent: "projects/testproject", Occurrence: o}
|
||||
if _, err := g.CreateOccurrence(ctx, oReq); err == nil {
|
||||
t.Errorf("CreateOccurrence got success, want Error")
|
||||
} else if s, _ := status.FromError(err); s.Code() != codes.NotFound {
|
||||
t.Errorf("CreateOccurrence got code %v want %v", err, codes.NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateNote(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
n := &pb.Note{}
|
||||
req := &pb.CreateNoteRequest{Parent: "projects/foo", Note: n}
|
||||
// Try to insert an empty note, expect failure
|
||||
if _, err := g.CreateNote(ctx, req); err == nil {
|
||||
t.Error("CreateNote(empty note): got success, want error")
|
||||
} else if s, _ := status.FromError(err); s.Code() != codes.InvalidArgument {
|
||||
t.Errorf("CreateNote(empty note): got %v, want %v", err, codes.InvalidArgument)
|
||||
}
|
||||
// Try to insert an onccurrence without first creating its project, expect failure
|
||||
pID := "vulnerability-scanner-a"
|
||||
n = testutil.Note(pID)
|
||||
parent := name.FormatProject(pID)
|
||||
req = &pb.CreateNoteRequest{Parent: parent, Note: n}
|
||||
if _, err := g.CreateNote(ctx, req); err == nil {
|
||||
t.Error("CreateNote: got success, want error")
|
||||
} else if s, _ := status.FromError(err); s.Code() != codes.NotFound {
|
||||
t.Errorf("CreateNote: got %v, want NotFound)", err)
|
||||
}
|
||||
createProject(t, pID, ctx, g)
|
||||
if _, err := g.CreateNote(ctx, req); err != nil {
|
||||
t.Errorf("CreateNote(%v) got %v, want success", n, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteProject(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
pID := "myproject"
|
||||
req := pb.DeleteProjectRequest{Name: name.FormatProject(pID)}
|
||||
if _, err := g.DeleteProject(ctx, &req); err == nil {
|
||||
t.Error("DeleteProject: got success, want error")
|
||||
}
|
||||
createProject(t, pID, ctx, g)
|
||||
if _, err := g.DeleteProject(ctx, &req); err != nil {
|
||||
t.Errorf("CreateProject(empty operation): got %v, want success", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteNote(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
pID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(pID)
|
||||
createProject(t, pID, ctx, g)
|
||||
req := &pb.DeleteNoteRequest{Name: n.Name}
|
||||
if _, err := g.DeleteNote(ctx, req); err == nil {
|
||||
t.Error("DeleteNote that doesn't exist got success, want err")
|
||||
}
|
||||
parent := name.FormatProject(pID)
|
||||
cReq := &pb.CreateNoteRequest{Parent: parent, Note: n}
|
||||
if _, err := g.CreateNote(ctx, cReq); err != nil {
|
||||
t.Errorf("CreateNote(%v) got %v, want success", n, err)
|
||||
}
|
||||
if _, err := g.DeleteNote(ctx, req); err != nil {
|
||||
t.Errorf("DeleteNote got %v, want success", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteOccurrence(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
pID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(pID)
|
||||
createProject(t, pID, ctx, g)
|
||||
parent := name.FormatProject(pID)
|
||||
cReq := &pb.CreateNoteRequest{Parent: parent, Note: n}
|
||||
// CreateNote so we can create an occurrence
|
||||
if _, err := g.CreateNote(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateNote(%v) got %v, want success", n, err)
|
||||
}
|
||||
pID = "occurrence-project"
|
||||
o := testutil.Occurrence(pID, n.Name)
|
||||
createProject(t, pID, ctx, g)
|
||||
|
||||
parent = name.FormatProject(pID)
|
||||
oReq := &pb.CreateOccurrenceRequest{Parent: parent, Occurrence: o}
|
||||
if _, err := g.CreateOccurrence(ctx, oReq); err != nil {
|
||||
t.Fatalf("CreateOccurrence(%v) got %v, want success", n, err)
|
||||
}
|
||||
dReq := &pb.DeleteOccurrenceRequest{Name: o.Name}
|
||||
if _, err := g.DeleteOccurrence(ctx, dReq); err != nil {
|
||||
t.Errorf("DeleteOccurrence got %v, want success", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteOperation(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
pID := "vulnerability-scanner-a"
|
||||
o := testutil.Operation(pID)
|
||||
createProject(t, pID, ctx, g)
|
||||
req := &opspb.DeleteOperationRequest{Name: o.Name}
|
||||
if _, err := g.DeleteOperation(ctx, req); err == nil {
|
||||
t.Error("DeleteOperation that doesn't exist got success, want err")
|
||||
}
|
||||
parent := name.FormatProject(pID)
|
||||
cReq := &pb.CreateOperationRequest{Parent: parent, Operation: o}
|
||||
if _, err := g.CreateOperation(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateOperation(%v) got %v, want success", o, err)
|
||||
}
|
||||
if _, err := g.DeleteOperation(ctx, req); err != nil {
|
||||
t.Errorf("DeleteOperation got %v, want success", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetProjects(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
pID := "myproject"
|
||||
req := pb.GetProjectRequest{Name: name.FormatProject(pID)}
|
||||
if _, err := g.GetProject(ctx, &req); err == nil {
|
||||
t.Error("GetProject that doesn't exist got success, want err")
|
||||
}
|
||||
createProject(t, pID, ctx, g)
|
||||
if _, err := g.GetProject(ctx, &req); err != nil {
|
||||
t.Errorf("GetProject: got %v, want success", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNote(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
pID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(pID)
|
||||
createProject(t, pID, ctx, g)
|
||||
req := &pb.GetNoteRequest{Name: n.Name}
|
||||
if _, err := g.GetNote(ctx, req); err == nil {
|
||||
t.Error("GetNote that doesn't exist got success, want err")
|
||||
}
|
||||
parent := name.FormatProject(pID)
|
||||
cReq := &pb.CreateNoteRequest{Parent: parent, Note: n}
|
||||
if _, err := g.CreateNote(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateNote(%v) got %v, want success", n, err)
|
||||
}
|
||||
if got, err := g.GetNote(ctx, req); err != nil {
|
||||
t.Fatalf("GetNote(%v) got %v, want success", n, err)
|
||||
} else if n.Name != got.Name || !reflect.DeepEqual(n.GetVulnerabilityType(), got.GetVulnerabilityType()) {
|
||||
t.Errorf("GetNote got %v, want %v", *got, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOccurrence(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
pID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(pID)
|
||||
createProject(t, pID, ctx, g)
|
||||
opID := "occurrence-project"
|
||||
o := testutil.Occurrence(opID, n.Name)
|
||||
createProject(t, opID, ctx, g)
|
||||
req := &pb.GetOccurrenceRequest{Name: o.Name}
|
||||
if _, err := g.GetOccurrence(ctx, req); err == nil {
|
||||
t.Error("GetOccurrence that doesn't exist got success, want err")
|
||||
}
|
||||
parent := name.FormatProject(pID)
|
||||
cReq := &pb.CreateNoteRequest{Parent: parent, Note: n}
|
||||
if _, err := g.CreateNote(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateNote(%v) got %v, want success", n, err)
|
||||
}
|
||||
oParent := name.FormatProject(opID)
|
||||
ocReq := &pb.CreateOccurrenceRequest{Parent: oParent, Occurrence: o}
|
||||
if _, err := g.CreateOccurrence(ctx, ocReq); err != nil {
|
||||
t.Fatalf("CreateOccurrence(%v) got %v, want success", n, err)
|
||||
}
|
||||
if got, err := g.GetOccurrence(ctx, req); err != nil {
|
||||
t.Fatalf("GetOccurrence(%v) got %v, want success", o, err)
|
||||
} else if o.Name != got.Name || !reflect.DeepEqual(o.GetVulnerabilityDetails(), got.GetVulnerabilityDetails()) {
|
||||
t.Errorf("GetOccurrence got %v, want %v", *got, o)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOperation(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
pID := "vulnerability-scanner-a"
|
||||
o := testutil.Operation(pID)
|
||||
createProject(t, pID, ctx, g)
|
||||
req := &opspb.GetOperationRequest{Name: o.Name}
|
||||
if _, err := g.GetOperation(ctx, req); err == nil {
|
||||
t.Error("GetOperation that doesn't exist got success, want err")
|
||||
}
|
||||
parent := name.FormatProject(pID)
|
||||
cReq := &pb.CreateOperationRequest{Parent: parent, Operation: o}
|
||||
if _, err := g.CreateOperation(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateOperation(%v) got %v, want success", o, err)
|
||||
}
|
||||
if got, err := g.GetOperation(ctx, req); err != nil {
|
||||
t.Fatalf("GetOperation(%v) got %v, want success", o, err)
|
||||
} else if o.Name != got.Name || !reflect.DeepEqual(got, o) {
|
||||
t.Errorf("GetOperation got %v, want %v", *got, o)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOccurrenceNote(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
pID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(pID)
|
||||
createProject(t, pID, ctx, g)
|
||||
opID := "occurrence-project"
|
||||
o := testutil.Occurrence(opID, n.Name)
|
||||
createProject(t, opID, ctx, g)
|
||||
|
||||
req := &pb.GetOccurrenceNoteRequest{Name: o.Name}
|
||||
if _, err := g.GetOccurrenceNote(ctx, req); err == nil {
|
||||
t.Error("GetOccurrenceNote that doesn't exist got success, want err")
|
||||
}
|
||||
pID, _, err := name.ParseNote(n.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing occurrence name %v", err)
|
||||
}
|
||||
parent := name.FormatProject(pID)
|
||||
cReq := &pb.CreateNoteRequest{Parent: parent, Note: n}
|
||||
|
||||
if _, err := g.CreateNote(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateNote(%v) got %v, want success", n, err)
|
||||
}
|
||||
parent = name.FormatProject(opID)
|
||||
coReq := &pb.CreateOccurrenceRequest{Parent: parent, Occurrence: o}
|
||||
if _, err := g.CreateOccurrence(ctx, coReq); err != nil {
|
||||
t.Fatalf("CreateOccurrence(%v) got %v, want success", n, err)
|
||||
}
|
||||
if got, err := g.GetOccurrenceNote(ctx, req); err != nil {
|
||||
t.Fatalf("GetOccurrenceNote(%v) got %v, want success", n, err)
|
||||
} else if n.Name != got.Name || !reflect.DeepEqual(n.GetVulnerabilityType(), got.GetVulnerabilityType()) {
|
||||
t.Errorf("GetOccurrenceNote got %v, want %v", *got, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateNote(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
// Update Note that doesn't exist
|
||||
updateDesc := "this is a new description"
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
pID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(pID)
|
||||
createProject(t, pID, ctx, g)
|
||||
update := testutil.Note(pID)
|
||||
update.LongDescription = updateDesc
|
||||
req := &pb.UpdateNoteRequest{Name: n.Name, Note: n}
|
||||
if _, err := g.UpdateNote(ctx, req); err != nil {
|
||||
t.Error("UpdateNote that doesn't exist got success, want err")
|
||||
}
|
||||
|
||||
parent := name.FormatProject(pID)
|
||||
cReq := &pb.CreateNoteRequest{Parent: parent, Note: n}
|
||||
// Actually create note
|
||||
if _, err := g.CreateNote(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateNote(%v) got %v, want success", n, err)
|
||||
}
|
||||
|
||||
// Update Note name and fail
|
||||
update.Name = "New name"
|
||||
req = &pb.UpdateNoteRequest{Name: n.Name, Note: update}
|
||||
if _, err := g.UpdateNote(ctx, req); err == nil {
|
||||
t.Error("UpdateNote that with name change got success, want err")
|
||||
}
|
||||
|
||||
// Update Note and verify that update worked.
|
||||
update = testutil.Note(pID)
|
||||
update.LongDescription = updateDesc
|
||||
req = &pb.UpdateNoteRequest{Name: n.Name, Note: update}
|
||||
if got, err := g.UpdateNote(ctx, req); err != nil {
|
||||
t.Errorf("UpdateNote got %v, want success", err)
|
||||
} else if updateDesc != update.LongDescription {
|
||||
t.Errorf("UpdateNote got %v, want %v",
|
||||
got.LongDescription, updateDesc)
|
||||
}
|
||||
if got, err := g.GetNote(ctx, &pb.GetNoteRequest{Name: n.Name}); err != nil {
|
||||
t.Fatalf("GetNote(%v) got %v, want success", n, err)
|
||||
} else if updateDesc != got.LongDescription {
|
||||
t.Errorf("GetNote got %v, want %v", got.LongDescription, updateDesc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateOccurrence(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
// Update occurrence that doesn't exist
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
npID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(npID)
|
||||
createProject(t, npID, ctx, g)
|
||||
nParent := name.FormatProject(npID)
|
||||
cReq := &pb.CreateNoteRequest{Parent: nParent, Note: n}
|
||||
|
||||
if _, err := g.CreateNote(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateNote(%v) got %v, want success", n, err)
|
||||
}
|
||||
pID := "occurrence-project"
|
||||
o := testutil.Occurrence(pID, n.Name)
|
||||
createProject(t, pID, ctx, g)
|
||||
|
||||
req := &pb.UpdateOccurrenceRequest{Name: o.Name, Occurrence: o}
|
||||
if _, err := g.UpdateOccurrence(ctx, req); err == nil {
|
||||
t.Error("UpdateOccurrence that doesn't exist got success, want err")
|
||||
}
|
||||
parent := name.FormatProject(pID)
|
||||
ocReq := &pb.CreateOccurrenceRequest{Parent: parent, Occurrence: o}
|
||||
if _, err := g.CreateOccurrence(ctx, ocReq); err != nil {
|
||||
t.Fatalf("CreateOccurrence(%v) got %v, want success", n, err)
|
||||
}
|
||||
// update occurrence name
|
||||
update := testutil.Occurrence(pID, n.Name)
|
||||
update.Name = "New name"
|
||||
req = &pb.UpdateOccurrenceRequest{Name: update.Name, Occurrence: update}
|
||||
if _, err := g.UpdateOccurrence(ctx, req); err == nil {
|
||||
t.Error("UpdateOccurrence with name change got success, want err")
|
||||
}
|
||||
|
||||
// update note name to a note that doesn't exist
|
||||
update = testutil.Occurrence(pID, "projects/p/notes/bar")
|
||||
req = &pb.UpdateOccurrenceRequest{Name: o.Name, Occurrence: update}
|
||||
if _, err := g.UpdateOccurrence(ctx, req); err == nil {
|
||||
t.Error("UpdateOccurrence that with note name that doesn't exist" +
|
||||
" got success, want err")
|
||||
}
|
||||
|
||||
// update note name to a note that does exist
|
||||
n = testutil.Note(npID)
|
||||
newName := fmt.Sprintf("%v-new", n.Name)
|
||||
n.Name = newName
|
||||
|
||||
cReq = &pb.CreateNoteRequest{Parent: nParent, Note: n}
|
||||
if _, err := g.CreateNote(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateNote(%v) got %v, want success", n, err)
|
||||
}
|
||||
update = testutil.Occurrence(pID, n.Name)
|
||||
req = &pb.UpdateOccurrenceRequest{Name: o.Name, Occurrence: update}
|
||||
if got, err := g.UpdateOccurrence(ctx, req); err != nil {
|
||||
t.Errorf("UpdateOccurrence got %v, want success", err)
|
||||
} else if n.Name != got.NoteName {
|
||||
t.Errorf("UpdateOccurrence got %v, want %v",
|
||||
got.NoteName, n.Name)
|
||||
}
|
||||
gReq := &pb.GetOccurrenceRequest{Name: o.Name}
|
||||
if got, err := g.GetOccurrence(ctx, gReq); err != nil {
|
||||
t.Fatalf("GetOccurrence(%v) got %v, want success", n, err)
|
||||
} else if n.Name != got.NoteName {
|
||||
t.Errorf("GetOccurrence got %v, want %v",
|
||||
got.NoteName, n.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListOccurrences(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
npID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(npID)
|
||||
nParent := name.FormatProject(npID)
|
||||
cReq := &pb.CreateNoteRequest{Parent: nParent, Note: n}
|
||||
createProject(t, npID, ctx, g)
|
||||
|
||||
if _, err := g.CreateNote(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateNote(%v) got %v, want success", n, err)
|
||||
}
|
||||
os := []*pb.Occurrence{}
|
||||
findProject := "findThese"
|
||||
createProject(t, findProject, ctx, g)
|
||||
dontFind := "dontFind"
|
||||
createProject(t, dontFind, ctx, g)
|
||||
for i := 0; i < 20; i++ {
|
||||
pID := "_"
|
||||
o := testutil.Occurrence(pID, n.Name)
|
||||
if i < 5 {
|
||||
o.Name = name.FormatOccurrence(findProject, string(i))
|
||||
} else {
|
||||
o.Name = name.FormatOccurrence(dontFind, string(i))
|
||||
}
|
||||
parent := name.FormatProject(pID)
|
||||
ocReq := &pb.CreateOccurrenceRequest{Parent: parent, Occurrence: o}
|
||||
if _, err := g.CreateOccurrence(ctx, ocReq); err != nil {
|
||||
t.Fatalf("CreateOccurrence got %v want success", err)
|
||||
}
|
||||
os = append(os, o)
|
||||
}
|
||||
|
||||
lReq := &pb.ListOccurrencesRequest{Parent: name.FormatProject(findProject)}
|
||||
resp, lErr := g.ListOccurrences(ctx, lReq)
|
||||
if lErr != nil {
|
||||
t.Fatalf("ListOccurrences got %v want success", lErr)
|
||||
}
|
||||
if len(resp.Occurrences) != 5 {
|
||||
t.Errorf("resp.Occurrences got %d, want 5", len(resp.Occurrences))
|
||||
}
|
||||
}
|
||||
|
||||
func TestListProjects(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
var projects []string
|
||||
for i := 0; i < 20; i++ {
|
||||
pID := fmt.Sprintf("proj%v", i)
|
||||
req := pb.CreateProjectRequest{Name: name.FormatProject(pID)}
|
||||
if _, err := g.CreateProject(ctx, &req); err != nil {
|
||||
t.Errorf("CreateProject: got %v, want success", err)
|
||||
}
|
||||
if _, err := g.CreateProject(ctx, &req); err == nil {
|
||||
t.Errorf("CreateProject: got %v, want InvalidArgument", err)
|
||||
}
|
||||
projects = append(projects, name.FormatProject(pID))
|
||||
}
|
||||
req := pb.ListProjectsRequest{}
|
||||
_, err := g.ListProjects(ctx, &req)
|
||||
if err != nil {
|
||||
t.Errorf("ListProjects: got %v, want success", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListOperations(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
findProject := "findThese"
|
||||
createProject(t, findProject, ctx, g)
|
||||
dontFind := "dontFind"
|
||||
createProject(t, dontFind, ctx, g)
|
||||
for i := 0; i < 20; i++ {
|
||||
pID := "vulnerability-scanner-a"
|
||||
o := testutil.Operation(pID)
|
||||
if i < 5 {
|
||||
o.Name = name.FormatOperation(findProject, string(i))
|
||||
} else {
|
||||
o.Name = name.FormatOperation(dontFind, string(i))
|
||||
}
|
||||
parent := name.FormatProject(pID)
|
||||
cReq := &pb.CreateOperationRequest{Parent: parent, Operation: o}
|
||||
if _, err := g.CreateOperation(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateOperation(%v) got %v, want success", o, err)
|
||||
}
|
||||
}
|
||||
|
||||
lReq := &opspb.ListOperationsRequest{Name: name.FormatProject(findProject)}
|
||||
resp, err := g.ListOperations(ctx, lReq)
|
||||
if err != nil {
|
||||
t.Fatalf("ListOperations got %v want success", err)
|
||||
}
|
||||
if len(resp.Operations) != 5 {
|
||||
t.Errorf("resp.Operations got %d, want 5", len(resp.Operations))
|
||||
}
|
||||
}
|
||||
|
||||
func TestListNotes(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
findProject := "findThese"
|
||||
createProject(t, findProject, ctx, g)
|
||||
dontFind := "dontFind"
|
||||
createProject(t, dontFind, ctx, g)
|
||||
for i := 0; i < 20; i++ {
|
||||
npID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(npID)
|
||||
if i < 5 {
|
||||
n.Name = name.FormatNote(findProject, string(i))
|
||||
} else {
|
||||
n.Name = name.FormatNote(dontFind, string(i))
|
||||
}
|
||||
nParent := name.FormatProject(npID)
|
||||
cReq := &pb.CreateNoteRequest{Parent: nParent, Note: n}
|
||||
if _, err := g.CreateNote(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateNote(%v) got %v, want success", n, err)
|
||||
}
|
||||
}
|
||||
|
||||
req := &pb.ListNotesRequest{Parent: name.FormatProject(findProject)}
|
||||
resp, err := g.ListNotes(ctx, req)
|
||||
if err != nil {
|
||||
t.Fatalf("ListNotes got %v want success", err)
|
||||
}
|
||||
if len(resp.Notes) != 5 {
|
||||
t.Errorf("resp.Notes got %d, want 5", len(resp.Notes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestListNoteOccurrences(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
g := Grafeas{storage.NewMemStore()}
|
||||
npID := "vulnerability-scanner-a"
|
||||
n := testutil.Note(npID)
|
||||
createProject(t, npID, ctx, g)
|
||||
nParent := name.FormatProject(npID)
|
||||
cReq := &pb.CreateNoteRequest{Parent: nParent, Note: n}
|
||||
if _, err := g.CreateNote(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateNote(%v) got %v, want success", n, err)
|
||||
}
|
||||
findProject := "findThese"
|
||||
createProject(t, findProject, ctx, g)
|
||||
dontFind := "dontFind"
|
||||
createProject(t, dontFind, ctx, g)
|
||||
for i := 0; i < 20; i++ {
|
||||
pID := "_"
|
||||
o := testutil.Occurrence(pID, n.Name)
|
||||
if i < 5 {
|
||||
o.Name = name.FormatOccurrence(findProject, string(i))
|
||||
} else {
|
||||
o.Name = name.FormatOccurrence(dontFind, string(i))
|
||||
}
|
||||
parent := name.FormatProject(pID)
|
||||
ocReq := &pb.CreateOccurrenceRequest{Parent: parent, Occurrence: o}
|
||||
if _, err := g.CreateOccurrence(ctx, ocReq); err != nil {
|
||||
t.Fatalf("CreateOccurrence got %v want success", err)
|
||||
}
|
||||
}
|
||||
// Create an occurrence tied to another note, to make sure we don't find it.
|
||||
otherN := testutil.Note("")
|
||||
otherN.Name = "projects/np/notes/not-to-find"
|
||||
npID, _, err := name.ParseNote(otherN.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing note name %v", err)
|
||||
}
|
||||
nParent = name.FormatProject(npID)
|
||||
createProject(t, npID, ctx, g)
|
||||
cReq = &pb.CreateNoteRequest{Parent: nParent, Note: otherN}
|
||||
if _, err := g.CreateNote(ctx, cReq); err != nil {
|
||||
t.Fatalf("CreateNote got %v want success", err)
|
||||
}
|
||||
pID := "occurrence-project"
|
||||
o := testutil.Occurrence(pID, otherN.Name)
|
||||
createProject(t, pID, ctx, g)
|
||||
parent := name.FormatProject(pID)
|
||||
ocReq := &pb.CreateOccurrenceRequest{Parent: parent, Occurrence: o}
|
||||
if _, err := g.CreateOccurrence(ctx, ocReq); err != nil {
|
||||
t.Fatalf("CreateOccurrence got %v want success", err)
|
||||
}
|
||||
pID, _, err = name.ParseNote(n.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing note name %v", err)
|
||||
}
|
||||
lReq := &pb.ListNoteOccurrencesRequest{Name: n.Name}
|
||||
resp, lErr := g.ListNoteOccurrences(ctx, lReq)
|
||||
if lErr != nil {
|
||||
t.Fatalf("ListNoteOccurrences got %v want success", err)
|
||||
}
|
||||
if len(resp.Occurrences) != 20 {
|
||||
t.Errorf("resp.Occurrences got %d, want 20", len(resp.Occurrences))
|
||||
}
|
||||
}
|
86
vendor/github.com/grafeas/grafeas/server-go/storage.go
generated
vendored
Normal file
86
vendor/github.com/grafeas/grafeas/server-go/storage.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2017 The Grafeas 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.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
pb "github.com/grafeas/grafeas/v1alpha1/proto"
|
||||
opspb "google.golang.org/genproto/googleapis/longrunning"
|
||||
)
|
||||
|
||||
// Storager is the interface that a Grafeas storage implementation would provide
|
||||
type Storager interface {
|
||||
// CreateProject adds the specified project
|
||||
CreateProject(pID string) error
|
||||
|
||||
// CreateNote adds the specified note
|
||||
CreateNote(n *pb.Note) error
|
||||
|
||||
// CreateOccurrence adds the specified occurrence
|
||||
CreateOccurrence(o *pb.Occurrence) error
|
||||
|
||||
// CreateOperation adds the specified operation
|
||||
CreateOperation(o *opspb.Operation) error
|
||||
|
||||
// DeleteNote deletes the project with the given pID
|
||||
DeleteProject(pID string) error
|
||||
|
||||
// DeleteNote deletes the note with the given pID and nID
|
||||
DeleteNote(pID, nID string) error
|
||||
|
||||
// DeleteOccurrence deletes the occurrence with the given pID and oID
|
||||
DeleteOccurrence(pID, oID string) error
|
||||
|
||||
// DeleteOperation deletes the operation with the given pID and oID
|
||||
DeleteOperation(pID, opID string) error
|
||||
|
||||
// GetProject returns the project with the given pID
|
||||
GetProject(pID string) (*pb.Project, error)
|
||||
|
||||
// GetNote returns the note with project (pID) and note ID (nID)
|
||||
GetNote(pID, nID string) (*pb.Note, error)
|
||||
|
||||
// GetNoteByOccurrence returns the note attached to occurrence with pID and oID
|
||||
GetNoteByOccurrence(pID, oID string) (*pb.Note, error)
|
||||
|
||||
// GetOccurrence returns the occurrence with pID and oID
|
||||
GetOccurrence(pID, oID string) (*pb.Occurrence, error)
|
||||
|
||||
// GetOperation returns the operation with pID and oID
|
||||
GetOperation(pID, opID string) (*opspb.Operation, error)
|
||||
|
||||
// ListProjects returns the project id for all projects
|
||||
ListProjects(filters string) []*pb.Project
|
||||
|
||||
// ListNoteOccurrences returns the occcurrences on the particular note (nID) for this project (pID)
|
||||
ListNoteOccurrences(pID, nID, filters string) ([]*pb.Occurrence, error)
|
||||
|
||||
// ListNotes returns the notes for for this project (pID)
|
||||
ListNotes(pID, filters string) []*pb.Note
|
||||
|
||||
// ListOccurrences returns the occurrences for this project ID (pID)
|
||||
ListOccurrences(pID, filters string) []*pb.Occurrence
|
||||
|
||||
// ListOperations returns the operations for this project (pID)
|
||||
ListOperations(pID, filters string) []*opspb.Operation
|
||||
|
||||
// UpdateNote updates the existing note with the given pID and nID
|
||||
UpdateNote(pID, nID string, n *pb.Note) error
|
||||
|
||||
// UpdateOccurrence updates the existing occurrence with the given projectID and occurrenceID
|
||||
UpdateOccurrence(pID, oID string, o *pb.Occurrence) error
|
||||
|
||||
// UpdateOperation updates the existing operation with the given pID and nID
|
||||
UpdateOperation(pID, opID string, op *opspb.Operation) error
|
||||
}
|
2007
vendor/github.com/grafeas/grafeas/v1alpha1/grafeas.json
generated
vendored
Normal file
2007
vendor/github.com/grafeas/grafeas/v1alpha1/grafeas.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5440
vendor/github.com/grafeas/grafeas/v1alpha1/proto/grafeas.pb.go
generated
vendored
Normal file
5440
vendor/github.com/grafeas/grafeas/v1alpha1/proto/grafeas.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1261
vendor/github.com/grafeas/grafeas/v1alpha1/proto/grafeas.pb.gw.go
generated
vendored
Normal file
1261
vendor/github.com/grafeas/grafeas/v1alpha1/proto/grafeas.pb.gw.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1454
vendor/github.com/grafeas/grafeas/v1alpha1/proto/grafeas.proto
generated
vendored
Normal file
1454
vendor/github.com/grafeas/grafeas/v1alpha1/proto/grafeas.proto
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1665
vendor/github.com/grafeas/grafeas/v1alpha1/proto/grafeas.swagger.json
generated
vendored
Normal file
1665
vendor/github.com/grafeas/grafeas/v1alpha1/proto/grafeas.swagger.json
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
15
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
15
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
@ -1,15 +0,0 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2012-2013 Dave Collins <dave@davec.name>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
152
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
152
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
@ -1,152 +0,0 @@
|
||||
// Copyright (c) 2015 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
||||
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build !js,!appengine,!safe,!disableunsafe
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||
// not access to the unsafe package is available.
|
||||
UnsafeDisabled = false
|
||||
|
||||
// ptrSize is the size of a pointer on the current arch.
|
||||
ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||
)
|
||||
|
||||
var (
|
||||
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
|
||||
// internal reflect.Value fields. These values are valid before golang
|
||||
// commit ecccf07e7f9d which changed the format. The are also valid
|
||||
// after commit 82f48826c6c7 which changed the format again to mirror
|
||||
// the original format. Code in the init function updates these offsets
|
||||
// as necessary.
|
||||
offsetPtr = uintptr(ptrSize)
|
||||
offsetScalar = uintptr(0)
|
||||
offsetFlag = uintptr(ptrSize * 2)
|
||||
|
||||
// flagKindWidth and flagKindShift indicate various bits that the
|
||||
// reflect package uses internally to track kind information.
|
||||
//
|
||||
// flagRO indicates whether or not the value field of a reflect.Value is
|
||||
// read-only.
|
||||
//
|
||||
// flagIndir indicates whether the value field of a reflect.Value is
|
||||
// the actual data or a pointer to the data.
|
||||
//
|
||||
// These values are valid before golang commit 90a7c3c86944 which
|
||||
// changed their positions. Code in the init function updates these
|
||||
// flags as necessary.
|
||||
flagKindWidth = uintptr(5)
|
||||
flagKindShift = uintptr(flagKindWidth - 1)
|
||||
flagRO = uintptr(1 << 0)
|
||||
flagIndir = uintptr(1 << 1)
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Older versions of reflect.Value stored small integers directly in the
|
||||
// ptr field (which is named val in the older versions). Versions
|
||||
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
|
||||
// scalar for this purpose which unfortunately came before the flag
|
||||
// field, so the offset of the flag field is different for those
|
||||
// versions.
|
||||
//
|
||||
// This code constructs a new reflect.Value from a known small integer
|
||||
// and checks if the size of the reflect.Value struct indicates it has
|
||||
// the scalar field. When it does, the offsets are updated accordingly.
|
||||
vv := reflect.ValueOf(0xf00)
|
||||
if unsafe.Sizeof(vv) == (ptrSize * 4) {
|
||||
offsetScalar = ptrSize * 2
|
||||
offsetFlag = ptrSize * 3
|
||||
}
|
||||
|
||||
// Commit 90a7c3c86944 changed the flag positions such that the low
|
||||
// order bits are the kind. This code extracts the kind from the flags
|
||||
// field and ensures it's the correct type. When it's not, the flag
|
||||
// order has been changed to the newer format, so the flags are updated
|
||||
// accordingly.
|
||||
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
|
||||
upfv := *(*uintptr)(upf)
|
||||
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
|
||||
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
|
||||
flagKindShift = 0
|
||||
flagRO = 1 << 5
|
||||
flagIndir = 1 << 6
|
||||
|
||||
// Commit adf9b30e5594 modified the flags to separate the
|
||||
// flagRO flag into two bits which specifies whether or not the
|
||||
// field is embedded. This causes flagIndir to move over a bit
|
||||
// and means that flagRO is the combination of either of the
|
||||
// original flagRO bit and the new bit.
|
||||
//
|
||||
// This code detects the change by extracting what used to be
|
||||
// the indirect bit to ensure it's set. When it's not, the flag
|
||||
// order has been changed to the newer format, so the flags are
|
||||
// updated accordingly.
|
||||
if upfv&flagIndir == 0 {
|
||||
flagRO = 3 << 5
|
||||
flagIndir = 1 << 7
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
||||
// the typical safety restrictions preventing access to unaddressable and
|
||||
// unexported data. It works by digging the raw pointer to the underlying
|
||||
// value out of the protected value and generating a new unprotected (unsafe)
|
||||
// reflect.Value to it.
|
||||
//
|
||||
// This allows us to check for implementations of the Stringer and error
|
||||
// interfaces to be used for pretty printing ordinarily unaddressable and
|
||||
// inaccessible values such as unexported struct fields.
|
||||
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
|
||||
indirects := 1
|
||||
vt := v.Type()
|
||||
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
|
||||
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
|
||||
if rvf&flagIndir != 0 {
|
||||
vt = reflect.PtrTo(v.Type())
|
||||
indirects++
|
||||
} else if offsetScalar != 0 {
|
||||
// The value is in the scalar field when it's not one of the
|
||||
// reference types.
|
||||
switch vt.Kind() {
|
||||
case reflect.Uintptr:
|
||||
case reflect.Chan:
|
||||
case reflect.Func:
|
||||
case reflect.Map:
|
||||
case reflect.Ptr:
|
||||
case reflect.UnsafePointer:
|
||||
default:
|
||||
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
|
||||
offsetScalar)
|
||||
}
|
||||
}
|
||||
|
||||
pv := reflect.NewAt(vt, upv)
|
||||
rv = pv
|
||||
for i := 0; i < indirects; i++ {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
return rv
|
||||
}
|
38
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
38
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
@ -1,38 +0,0 @@
|
||||
// Copyright (c) 2015 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when the code is running on Google App Engine, compiled by GopherJS, or
|
||||
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build js appengine safe disableunsafe
|
||||
|
||||
package spew
|
||||
|
||||
import "reflect"
|
||||
|
||||
const (
|
||||
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||
// not access to the unsafe package is available.
|
||||
UnsafeDisabled = true
|
||||
)
|
||||
|
||||
// unsafeReflectValue typically converts the passed reflect.Value into a one
|
||||
// that bypasses the typical safety restrictions preventing access to
|
||||
// unaddressable and unexported data. However, doing this relies on access to
|
||||
// the unsafe package. This is a stub version which simply returns the passed
|
||||
// reflect.Value when the unsafe package is not available.
|
||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||
return v
|
||||
}
|
341
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
341
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
@ -1,341 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Some constants in the form of bytes to avoid string overhead. This mirrors
|
||||
// the technique used in the fmt package.
|
||||
var (
|
||||
panicBytes = []byte("(PANIC=")
|
||||
plusBytes = []byte("+")
|
||||
iBytes = []byte("i")
|
||||
trueBytes = []byte("true")
|
||||
falseBytes = []byte("false")
|
||||
interfaceBytes = []byte("(interface {})")
|
||||
commaNewlineBytes = []byte(",\n")
|
||||
newlineBytes = []byte("\n")
|
||||
openBraceBytes = []byte("{")
|
||||
openBraceNewlineBytes = []byte("{\n")
|
||||
closeBraceBytes = []byte("}")
|
||||
asteriskBytes = []byte("*")
|
||||
colonBytes = []byte(":")
|
||||
colonSpaceBytes = []byte(": ")
|
||||
openParenBytes = []byte("(")
|
||||
closeParenBytes = []byte(")")
|
||||
spaceBytes = []byte(" ")
|
||||
pointerChainBytes = []byte("->")
|
||||
nilAngleBytes = []byte("<nil>")
|
||||
maxNewlineBytes = []byte("<max depth reached>\n")
|
||||
maxShortBytes = []byte("<max>")
|
||||
circularBytes = []byte("<already shown>")
|
||||
circularShortBytes = []byte("<shown>")
|
||||
invalidAngleBytes = []byte("<invalid>")
|
||||
openBracketBytes = []byte("[")
|
||||
closeBracketBytes = []byte("]")
|
||||
percentBytes = []byte("%")
|
||||
precisionBytes = []byte(".")
|
||||
openAngleBytes = []byte("<")
|
||||
closeAngleBytes = []byte(">")
|
||||
openMapBytes = []byte("map[")
|
||||
closeMapBytes = []byte("]")
|
||||
lenEqualsBytes = []byte("len=")
|
||||
capEqualsBytes = []byte("cap=")
|
||||
)
|
||||
|
||||
// hexDigits is used to map a decimal value to a hex digit.
|
||||
var hexDigits = "0123456789abcdef"
|
||||
|
||||
// catchPanic handles any panics that might occur during the handleMethods
|
||||
// calls.
|
||||
func catchPanic(w io.Writer, v reflect.Value) {
|
||||
if err := recover(); err != nil {
|
||||
w.Write(panicBytes)
|
||||
fmt.Fprintf(w, "%v", err)
|
||||
w.Write(closeParenBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// handleMethods attempts to call the Error and String methods on the underlying
|
||||
// type the passed reflect.Value represents and outputes the result to Writer w.
|
||||
//
|
||||
// It handles panics in any called methods by catching and displaying the error
|
||||
// as the formatted value.
|
||||
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
|
||||
// We need an interface to check if the type implements the error or
|
||||
// Stringer interface. However, the reflect package won't give us an
|
||||
// interface on certain things like unexported struct fields in order
|
||||
// to enforce visibility rules. We use unsafe, when it's available,
|
||||
// to bypass these restrictions since this package does not mutate the
|
||||
// values.
|
||||
if !v.CanInterface() {
|
||||
if UnsafeDisabled {
|
||||
return false
|
||||
}
|
||||
|
||||
v = unsafeReflectValue(v)
|
||||
}
|
||||
|
||||
// Choose whether or not to do error and Stringer interface lookups against
|
||||
// the base type or a pointer to the base type depending on settings.
|
||||
// Technically calling one of these methods with a pointer receiver can
|
||||
// mutate the value, however, types which choose to satisify an error or
|
||||
// Stringer interface with a pointer receiver should not be mutating their
|
||||
// state inside these interface methods.
|
||||
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
|
||||
v = unsafeReflectValue(v)
|
||||
}
|
||||
if v.CanAddr() {
|
||||
v = v.Addr()
|
||||
}
|
||||
|
||||
// Is it an error or Stringer?
|
||||
switch iface := v.Interface().(type) {
|
||||
case error:
|
||||
defer catchPanic(w, v)
|
||||
if cs.ContinueOnMethod {
|
||||
w.Write(openParenBytes)
|
||||
w.Write([]byte(iface.Error()))
|
||||
w.Write(closeParenBytes)
|
||||
w.Write(spaceBytes)
|
||||
return false
|
||||
}
|
||||
|
||||
w.Write([]byte(iface.Error()))
|
||||
return true
|
||||
|
||||
case fmt.Stringer:
|
||||
defer catchPanic(w, v)
|
||||
if cs.ContinueOnMethod {
|
||||
w.Write(openParenBytes)
|
||||
w.Write([]byte(iface.String()))
|
||||
w.Write(closeParenBytes)
|
||||
w.Write(spaceBytes)
|
||||
return false
|
||||
}
|
||||
w.Write([]byte(iface.String()))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// printBool outputs a boolean value as true or false to Writer w.
|
||||
func printBool(w io.Writer, val bool) {
|
||||
if val {
|
||||
w.Write(trueBytes)
|
||||
} else {
|
||||
w.Write(falseBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// printInt outputs a signed integer value to Writer w.
|
||||
func printInt(w io.Writer, val int64, base int) {
|
||||
w.Write([]byte(strconv.FormatInt(val, base)))
|
||||
}
|
||||
|
||||
// printUint outputs an unsigned integer value to Writer w.
|
||||
func printUint(w io.Writer, val uint64, base int) {
|
||||
w.Write([]byte(strconv.FormatUint(val, base)))
|
||||
}
|
||||
|
||||
// printFloat outputs a floating point value using the specified precision,
|
||||
// which is expected to be 32 or 64bit, to Writer w.
|
||||
func printFloat(w io.Writer, val float64, precision int) {
|
||||
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
|
||||
}
|
||||
|
||||
// printComplex outputs a complex value using the specified float precision
|
||||
// for the real and imaginary parts to Writer w.
|
||||
func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
||||
r := real(c)
|
||||
w.Write(openParenBytes)
|
||||
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
|
||||
i := imag(c)
|
||||
if i >= 0 {
|
||||
w.Write(plusBytes)
|
||||
}
|
||||
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
|
||||
w.Write(iBytes)
|
||||
w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
|
||||
// prefix to Writer w.
|
||||
func printHexPtr(w io.Writer, p uintptr) {
|
||||
// Null pointer.
|
||||
num := uint64(p)
|
||||
if num == 0 {
|
||||
w.Write(nilAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
|
||||
buf := make([]byte, 18)
|
||||
|
||||
// It's simpler to construct the hex string right to left.
|
||||
base := uint64(16)
|
||||
i := len(buf) - 1
|
||||
for num >= base {
|
||||
buf[i] = hexDigits[num%base]
|
||||
num /= base
|
||||
i--
|
||||
}
|
||||
buf[i] = hexDigits[num]
|
||||
|
||||
// Add '0x' prefix.
|
||||
i--
|
||||
buf[i] = 'x'
|
||||
i--
|
||||
buf[i] = '0'
|
||||
|
||||
// Strip unused leading bytes.
|
||||
buf = buf[i:]
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
|
||||
// elements to be sorted.
|
||||
type valuesSorter struct {
|
||||
values []reflect.Value
|
||||
strings []string // either nil or same len and values
|
||||
cs *ConfigState
|
||||
}
|
||||
|
||||
// newValuesSorter initializes a valuesSorter instance, which holds a set of
|
||||
// surrogate keys on which the data should be sorted. It uses flags in
|
||||
// ConfigState to decide if and how to populate those surrogate keys.
|
||||
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
|
||||
vs := &valuesSorter{values: values, cs: cs}
|
||||
if canSortSimply(vs.values[0].Kind()) {
|
||||
return vs
|
||||
}
|
||||
if !cs.DisableMethods {
|
||||
vs.strings = make([]string, len(values))
|
||||
for i := range vs.values {
|
||||
b := bytes.Buffer{}
|
||||
if !handleMethods(cs, &b, vs.values[i]) {
|
||||
vs.strings = nil
|
||||
break
|
||||
}
|
||||
vs.strings[i] = b.String()
|
||||
}
|
||||
}
|
||||
if vs.strings == nil && cs.SpewKeys {
|
||||
vs.strings = make([]string, len(values))
|
||||
for i := range vs.values {
|
||||
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
|
||||
}
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
|
||||
// directly, or whether it should be considered for sorting by surrogate keys
|
||||
// (if the ConfigState allows it).
|
||||
func canSortSimply(kind reflect.Kind) bool {
|
||||
// This switch parallels valueSortLess, except for the default case.
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
return true
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
return true
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
return true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return true
|
||||
case reflect.String:
|
||||
return true
|
||||
case reflect.Uintptr:
|
||||
return true
|
||||
case reflect.Array:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Len returns the number of values in the slice. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s *valuesSorter) Len() int {
|
||||
return len(s.values)
|
||||
}
|
||||
|
||||
// Swap swaps the values at the passed indices. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s *valuesSorter) Swap(i, j int) {
|
||||
s.values[i], s.values[j] = s.values[j], s.values[i]
|
||||
if s.strings != nil {
|
||||
s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
|
||||
}
|
||||
}
|
||||
|
||||
// valueSortLess returns whether the first value should sort before the second
|
||||
// value. It is used by valueSorter.Less as part of the sort.Interface
|
||||
// implementation.
|
||||
func valueSortLess(a, b reflect.Value) bool {
|
||||
switch a.Kind() {
|
||||
case reflect.Bool:
|
||||
return !a.Bool() && b.Bool()
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
return a.Int() < b.Int()
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
return a.Uint() < b.Uint()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return a.Float() < b.Float()
|
||||
case reflect.String:
|
||||
return a.String() < b.String()
|
||||
case reflect.Uintptr:
|
||||
return a.Uint() < b.Uint()
|
||||
case reflect.Array:
|
||||
// Compare the contents of both arrays.
|
||||
l := a.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
av := a.Index(i)
|
||||
bv := b.Index(i)
|
||||
if av.Interface() == bv.Interface() {
|
||||
continue
|
||||
}
|
||||
return valueSortLess(av, bv)
|
||||
}
|
||||
}
|
||||
return a.String() < b.String()
|
||||
}
|
||||
|
||||
// Less returns whether the value at index i should sort before the
|
||||
// value at index j. It is part of the sort.Interface implementation.
|
||||
func (s *valuesSorter) Less(i, j int) bool {
|
||||
if s.strings == nil {
|
||||
return valueSortLess(s.values[i], s.values[j])
|
||||
}
|
||||
return s.strings[i] < s.strings[j]
|
||||
}
|
||||
|
||||
// sortValues is a sort function that handles both native types and any type that
|
||||
// can be converted to error or Stringer. Other inputs are sorted according to
|
||||
// their Value.String() value to ensure display stability.
|
||||
func sortValues(values []reflect.Value, cs *ConfigState) {
|
||||
if len(values) == 0 {
|
||||
return
|
||||
}
|
||||
sort.Sort(newValuesSorter(values, cs))
|
||||
}
|
297
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
297
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
@ -1,297 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ConfigState houses the configuration options used by spew to format and
|
||||
// display values. There is a global instance, Config, that is used to control
|
||||
// all top-level Formatter and Dump functionality. Each ConfigState instance
|
||||
// provides methods equivalent to the top-level functions.
|
||||
//
|
||||
// The zero value for ConfigState provides no indentation. You would typically
|
||||
// want to set it to a space or a tab.
|
||||
//
|
||||
// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
|
||||
// with default settings. See the documentation of NewDefaultConfig for default
|
||||
// values.
|
||||
type ConfigState struct {
|
||||
// Indent specifies the string to use for each indentation level. The
|
||||
// global config instance that all top-level functions use set this to a
|
||||
// single space by default. If you would like more indentation, you might
|
||||
// set this to a tab with "\t" or perhaps two spaces with " ".
|
||||
Indent string
|
||||
|
||||
// MaxDepth controls the maximum number of levels to descend into nested
|
||||
// data structures. The default, 0, means there is no limit.
|
||||
//
|
||||
// NOTE: Circular data structures are properly detected, so it is not
|
||||
// necessary to set this value unless you specifically want to limit deeply
|
||||
// nested data structures.
|
||||
MaxDepth int
|
||||
|
||||
// DisableMethods specifies whether or not error and Stringer interfaces are
|
||||
// invoked for types that implement them.
|
||||
DisableMethods bool
|
||||
|
||||
// DisablePointerMethods specifies whether or not to check for and invoke
|
||||
// error and Stringer interfaces on types which only accept a pointer
|
||||
// receiver when the current type is not a pointer.
|
||||
//
|
||||
// NOTE: This might be an unsafe action since calling one of these methods
|
||||
// with a pointer receiver could technically mutate the value, however,
|
||||
// in practice, types which choose to satisify an error or Stringer
|
||||
// interface with a pointer receiver should not be mutating their state
|
||||
// inside these interface methods. As a result, this option relies on
|
||||
// access to the unsafe package, so it will not have any effect when
|
||||
// running in environments without access to the unsafe package such as
|
||||
// Google App Engine or with the "safe" build tag specified.
|
||||
DisablePointerMethods bool
|
||||
|
||||
// ContinueOnMethod specifies whether or not recursion should continue once
|
||||
// a custom error or Stringer interface is invoked. The default, false,
|
||||
// means it will print the results of invoking the custom error or Stringer
|
||||
// interface and return immediately instead of continuing to recurse into
|
||||
// the internals of the data type.
|
||||
//
|
||||
// NOTE: This flag does not have any effect if method invocation is disabled
|
||||
// via the DisableMethods or DisablePointerMethods options.
|
||||
ContinueOnMethod bool
|
||||
|
||||
// SortKeys specifies map keys should be sorted before being printed. Use
|
||||
// this to have a more deterministic, diffable output. Note that only
|
||||
// native types (bool, int, uint, floats, uintptr and string) and types
|
||||
// that support the error or Stringer interfaces (if methods are
|
||||
// enabled) are supported, with other types sorted according to the
|
||||
// reflect.Value.String() output which guarantees display stability.
|
||||
SortKeys bool
|
||||
|
||||
// SpewKeys specifies that, as a last resort attempt, map keys should
|
||||
// be spewed to strings and sorted by those strings. This is only
|
||||
// considered if SortKeys is true.
|
||||
SpewKeys bool
|
||||
}
|
||||
|
||||
// Config is the active configuration of the top-level functions.
|
||||
// The configuration can be changed by modifying the contents of spew.Config.
|
||||
var Config = ConfigState{Indent: " "}
|
||||
|
||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the formatted string as a value that satisfies error. See NewFormatter
|
||||
// for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
|
||||
return fmt.Errorf(format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprint(w, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintf(w, format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
||||
// passed with a Formatter interface returned by c.NewFormatter. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(w, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
|
||||
return fmt.Print(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Printf(format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
|
||||
return fmt.Println(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Sprint(a ...interface{}) string {
|
||||
return fmt.Sprint(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
|
||||
return fmt.Sprintf(format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
||||
// were passed with a Formatter interface returned by c.NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Sprintln(a ...interface{}) string {
|
||||
return fmt.Sprintln(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
/*
|
||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||
interface. As a result, it integrates cleanly with standard fmt package
|
||||
printing functions. The formatter is useful for inline printing of smaller data
|
||||
types similar to the standard %v format specifier.
|
||||
|
||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
|
||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||
the width and precision arguments (however they will still work on the format
|
||||
specifiers not handled by the custom formatter).
|
||||
|
||||
Typically this function shouldn't be called directly. It is much easier to make
|
||||
use of the custom formatter by calling one of the convenience functions such as
|
||||
c.Printf, c.Println, or c.Printf.
|
||||
*/
|
||||
func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
|
||||
return newFormatter(c, v)
|
||||
}
|
||||
|
||||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||
// exactly the same as Dump.
|
||||
func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
|
||||
fdump(c, w, a...)
|
||||
}
|
||||
|
||||
/*
|
||||
Dump displays the passed parameters to standard out with newlines, customizable
|
||||
indentation, and additional debug information such as complete types and all
|
||||
pointer addresses used to indirect to the final value. It provides the
|
||||
following features over the built-in printing facilities provided by the fmt
|
||||
package:
|
||||
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output
|
||||
|
||||
The configuration options are controlled by modifying the public members
|
||||
of c. See ConfigState for options documentation.
|
||||
|
||||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
||||
get the formatted result as a string.
|
||||
*/
|
||||
func (c *ConfigState) Dump(a ...interface{}) {
|
||||
fdump(c, os.Stdout, a...)
|
||||
}
|
||||
|
||||
// Sdump returns a string with the passed arguments formatted exactly the same
|
||||
// as Dump.
|
||||
func (c *ConfigState) Sdump(a ...interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
fdump(c, &buf, a...)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// convertArgs accepts a slice of arguments and returns a slice of the same
|
||||
// length with each argument converted to a spew Formatter interface using
|
||||
// the ConfigState associated with s.
|
||||
func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
|
||||
formatters = make([]interface{}, len(args))
|
||||
for index, arg := range args {
|
||||
formatters[index] = newFormatter(c, arg)
|
||||
}
|
||||
return formatters
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a ConfigState with the following default settings.
|
||||
//
|
||||
// Indent: " "
|
||||
// MaxDepth: 0
|
||||
// DisableMethods: false
|
||||
// DisablePointerMethods: false
|
||||
// ContinueOnMethod: false
|
||||
// SortKeys: false
|
||||
func NewDefaultConfig() *ConfigState {
|
||||
return &ConfigState{Indent: " "}
|
||||
}
|
202
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
202
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
@ -1,202 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
Package spew implements a deep pretty printer for Go data structures to aid in
|
||||
debugging.
|
||||
|
||||
A quick overview of the additional features spew provides over the built-in
|
||||
printing facilities for Go data types are as follows:
|
||||
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output (only when using
|
||||
Dump style)
|
||||
|
||||
There are two different approaches spew allows for dumping Go data structures:
|
||||
|
||||
* Dump style which prints with newlines, customizable indentation,
|
||||
and additional debug information such as types and all pointer addresses
|
||||
used to indirect to the final value
|
||||
* A custom Formatter interface that integrates cleanly with the standard fmt
|
||||
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
|
||||
similar to the default %v while providing the additional functionality
|
||||
outlined above and passing unsupported format verbs such as %x and %q
|
||||
along to fmt
|
||||
|
||||
Quick Start
|
||||
|
||||
This section demonstrates how to quickly get started with spew. See the
|
||||
sections below for further details on formatting and configuration options.
|
||||
|
||||
To dump a variable with full newlines, indentation, type, and pointer
|
||||
information use Dump, Fdump, or Sdump:
|
||||
spew.Dump(myVar1, myVar2, ...)
|
||||
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
||||
str := spew.Sdump(myVar1, myVar2, ...)
|
||||
|
||||
Alternatively, if you would prefer to use format strings with a compacted inline
|
||||
printing style, use the convenience wrappers Printf, Fprintf, etc with
|
||||
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
|
||||
%#+v (adds types and pointer addresses):
|
||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
|
||||
Configuration Options
|
||||
|
||||
Configuration of spew is handled by fields in the ConfigState type. For
|
||||
convenience, all of the top-level functions use a global state available
|
||||
via the spew.Config global.
|
||||
|
||||
It is also possible to create a ConfigState instance that provides methods
|
||||
equivalent to the top-level functions. This allows concurrent configuration
|
||||
options. See the ConfigState documentation for more details.
|
||||
|
||||
The following configuration options are available:
|
||||
* Indent
|
||||
String to use for each indentation level for Dump functions.
|
||||
It is a single space by default. A popular alternative is "\t".
|
||||
|
||||
* MaxDepth
|
||||
Maximum number of levels to descend into nested data structures.
|
||||
There is no limit by default.
|
||||
|
||||
* DisableMethods
|
||||
Disables invocation of error and Stringer interface methods.
|
||||
Method invocation is enabled by default.
|
||||
|
||||
* DisablePointerMethods
|
||||
Disables invocation of error and Stringer interface methods on types
|
||||
which only accept pointer receivers from non-pointer variables.
|
||||
Pointer method invocation is enabled by default.
|
||||
|
||||
* ContinueOnMethod
|
||||
Enables recursion into types after invoking error and Stringer interface
|
||||
methods. Recursion after method invocation is disabled by default.
|
||||
|
||||
* SortKeys
|
||||
Specifies map keys should be sorted before being printed. Use
|
||||
this to have a more deterministic, diffable output. Note that
|
||||
only native types (bool, int, uint, floats, uintptr and string)
|
||||
and types which implement error or Stringer interfaces are
|
||||
supported with other types sorted according to the
|
||||
reflect.Value.String() output which guarantees display
|
||||
stability. Natural map order is used by default.
|
||||
|
||||
* SpewKeys
|
||||
Specifies that, as a last resort attempt, map keys should be
|
||||
spewed to strings and sorted by those strings. This is only
|
||||
considered if SortKeys is true.
|
||||
|
||||
Dump Usage
|
||||
|
||||
Simply call spew.Dump with a list of variables you want to dump:
|
||||
|
||||
spew.Dump(myVar1, myVar2, ...)
|
||||
|
||||
You may also call spew.Fdump if you would prefer to output to an arbitrary
|
||||
io.Writer. For example, to dump to standard error:
|
||||
|
||||
spew.Fdump(os.Stderr, myVar1, myVar2, ...)
|
||||
|
||||
A third option is to call spew.Sdump to get the formatted output as a string:
|
||||
|
||||
str := spew.Sdump(myVar1, myVar2, ...)
|
||||
|
||||
Sample Dump Output
|
||||
|
||||
See the Dump example for details on the setup of the types and variables being
|
||||
shown here.
|
||||
|
||||
(main.Foo) {
|
||||
unexportedField: (*main.Bar)(0xf84002e210)({
|
||||
flag: (main.Flag) flagTwo,
|
||||
data: (uintptr) <nil>
|
||||
}),
|
||||
ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||
(string) (len=3) "one": (bool) true
|
||||
}
|
||||
}
|
||||
|
||||
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
|
||||
command as shown.
|
||||
([]uint8) (len=32 cap=32) {
|
||||
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||
00000020 31 32 |12|
|
||||
}
|
||||
|
||||
Custom Formatter
|
||||
|
||||
Spew provides a custom formatter that implements the fmt.Formatter interface
|
||||
so that it integrates cleanly with standard fmt package printing functions. The
|
||||
formatter is useful for inline printing of smaller data types similar to the
|
||||
standard %v format specifier.
|
||||
|
||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||
the width and precision arguments (however they will still work on the format
|
||||
specifiers not handled by the custom formatter).
|
||||
|
||||
Custom Formatter Usage
|
||||
|
||||
The simplest way to make use of the spew custom formatter is to call one of the
|
||||
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
|
||||
functions have syntax you are most likely already familiar with:
|
||||
|
||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
spew.Println(myVar, myVar2)
|
||||
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
|
||||
See the Index for the full list convenience functions.
|
||||
|
||||
Sample Formatter Output
|
||||
|
||||
Double pointer to a uint8:
|
||||
%v: <**>5
|
||||
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
||||
%#v: (**uint8)5
|
||||
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
||||
|
||||
Pointer to circular struct with a uint8 field and a pointer to itself:
|
||||
%v: <*>{1 <*><shown>}
|
||||
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
||||
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
||||
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
||||
|
||||
See the Printf example for details on the setup of variables being shown
|
||||
here.
|
||||
|
||||
Errors
|
||||
|
||||
Since it is possible for custom Stringer/error interfaces to panic, spew
|
||||
detects them and handles them internally by printing the panic information
|
||||
inline with the output. Since spew is intended to provide deep pretty printing
|
||||
capabilities on structures, it intentionally does not return any errors.
|
||||
*/
|
||||
package spew
|
509
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
509
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
@ -1,509 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// uint8Type is a reflect.Type representing a uint8. It is used to
|
||||
// convert cgo types to uint8 slices for hexdumping.
|
||||
uint8Type = reflect.TypeOf(uint8(0))
|
||||
|
||||
// cCharRE is a regular expression that matches a cgo char.
|
||||
// It is used to detect character arrays to hexdump them.
|
||||
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
|
||||
|
||||
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
||||
// char. It is used to detect unsigned character arrays to hexdump
|
||||
// them.
|
||||
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
|
||||
|
||||
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
||||
// It is used to detect uint8_t arrays to hexdump them.
|
||||
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
|
||||
)
|
||||
|
||||
// dumpState contains information about the state of a dump operation.
|
||||
type dumpState struct {
|
||||
w io.Writer
|
||||
depth int
|
||||
pointers map[uintptr]int
|
||||
ignoreNextType bool
|
||||
ignoreNextIndent bool
|
||||
cs *ConfigState
|
||||
}
|
||||
|
||||
// indent performs indentation according to the depth level and cs.Indent
|
||||
// option.
|
||||
func (d *dumpState) indent() {
|
||||
if d.ignoreNextIndent {
|
||||
d.ignoreNextIndent = false
|
||||
return
|
||||
}
|
||||
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
|
||||
}
|
||||
|
||||
// unpackValue returns values inside of non-nil interfaces when possible.
|
||||
// This is useful for data types like structs, arrays, slices, and maps which
|
||||
// can contain varying types packed inside an interface.
|
||||
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
|
||||
if v.Kind() == reflect.Interface && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// dumpPtr handles formatting of pointers by indirecting them as necessary.
|
||||
func (d *dumpState) dumpPtr(v reflect.Value) {
|
||||
// Remove pointers at or below the current depth from map used to detect
|
||||
// circular refs.
|
||||
for k, depth := range d.pointers {
|
||||
if depth >= d.depth {
|
||||
delete(d.pointers, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Keep list of all dereferenced pointers to show later.
|
||||
pointerChain := make([]uintptr, 0)
|
||||
|
||||
// Figure out how many levels of indirection there are by dereferencing
|
||||
// pointers and unpacking interfaces down the chain while detecting circular
|
||||
// references.
|
||||
nilFound := false
|
||||
cycleFound := false
|
||||
indirects := 0
|
||||
ve := v
|
||||
for ve.Kind() == reflect.Ptr {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
indirects++
|
||||
addr := ve.Pointer()
|
||||
pointerChain = append(pointerChain, addr)
|
||||
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
|
||||
cycleFound = true
|
||||
indirects--
|
||||
break
|
||||
}
|
||||
d.pointers[addr] = d.depth
|
||||
|
||||
ve = ve.Elem()
|
||||
if ve.Kind() == reflect.Interface {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
ve = ve.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
// Display type information.
|
||||
d.w.Write(openParenBytes)
|
||||
d.w.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||
d.w.Write([]byte(ve.Type().String()))
|
||||
d.w.Write(closeParenBytes)
|
||||
|
||||
// Display pointer information.
|
||||
if len(pointerChain) > 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
for i, addr := range pointerChain {
|
||||
if i > 0 {
|
||||
d.w.Write(pointerChainBytes)
|
||||
}
|
||||
printHexPtr(d.w, addr)
|
||||
}
|
||||
d.w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// Display dereferenced value.
|
||||
d.w.Write(openParenBytes)
|
||||
switch {
|
||||
case nilFound == true:
|
||||
d.w.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound == true:
|
||||
d.w.Write(circularBytes)
|
||||
|
||||
default:
|
||||
d.ignoreNextType = true
|
||||
d.dump(ve)
|
||||
}
|
||||
d.w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
|
||||
// reflection) arrays and slices are dumped in hexdump -C fashion.
|
||||
func (d *dumpState) dumpSlice(v reflect.Value) {
|
||||
// Determine whether this type should be hex dumped or not. Also,
|
||||
// for types which should be hexdumped, try to use the underlying data
|
||||
// first, then fall back to trying to convert them to a uint8 slice.
|
||||
var buf []uint8
|
||||
doConvert := false
|
||||
doHexDump := false
|
||||
numEntries := v.Len()
|
||||
if numEntries > 0 {
|
||||
vt := v.Index(0).Type()
|
||||
vts := vt.String()
|
||||
switch {
|
||||
// C types that need to be converted.
|
||||
case cCharRE.MatchString(vts):
|
||||
fallthrough
|
||||
case cUnsignedCharRE.MatchString(vts):
|
||||
fallthrough
|
||||
case cUint8tCharRE.MatchString(vts):
|
||||
doConvert = true
|
||||
|
||||
// Try to use existing uint8 slices and fall back to converting
|
||||
// and copying if that fails.
|
||||
case vt.Kind() == reflect.Uint8:
|
||||
// We need an addressable interface to convert the type
|
||||
// to a byte slice. However, the reflect package won't
|
||||
// give us an interface on certain things like
|
||||
// unexported struct fields in order to enforce
|
||||
// visibility rules. We use unsafe, when available, to
|
||||
// bypass these restrictions since this package does not
|
||||
// mutate the values.
|
||||
vs := v
|
||||
if !vs.CanInterface() || !vs.CanAddr() {
|
||||
vs = unsafeReflectValue(vs)
|
||||
}
|
||||
if !UnsafeDisabled {
|
||||
vs = vs.Slice(0, numEntries)
|
||||
|
||||
// Use the existing uint8 slice if it can be
|
||||
// type asserted.
|
||||
iface := vs.Interface()
|
||||
if slice, ok := iface.([]uint8); ok {
|
||||
buf = slice
|
||||
doHexDump = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// The underlying data needs to be converted if it can't
|
||||
// be type asserted to a uint8 slice.
|
||||
doConvert = true
|
||||
}
|
||||
|
||||
// Copy and convert the underlying type if needed.
|
||||
if doConvert && vt.ConvertibleTo(uint8Type) {
|
||||
// Convert and copy each element into a uint8 byte
|
||||
// slice.
|
||||
buf = make([]uint8, numEntries)
|
||||
for i := 0; i < numEntries; i++ {
|
||||
vv := v.Index(i)
|
||||
buf[i] = uint8(vv.Convert(uint8Type).Uint())
|
||||
}
|
||||
doHexDump = true
|
||||
}
|
||||
}
|
||||
|
||||
// Hexdump the entire slice as needed.
|
||||
if doHexDump {
|
||||
indent := strings.Repeat(d.cs.Indent, d.depth)
|
||||
str := indent + hex.Dump(buf)
|
||||
str = strings.Replace(str, "\n", "\n"+indent, -1)
|
||||
str = strings.TrimRight(str, d.cs.Indent)
|
||||
d.w.Write([]byte(str))
|
||||
return
|
||||
}
|
||||
|
||||
// Recursively call dump for each item.
|
||||
for i := 0; i < numEntries; i++ {
|
||||
d.dump(d.unpackValue(v.Index(i)))
|
||||
if i < (numEntries - 1) {
|
||||
d.w.Write(commaNewlineBytes)
|
||||
} else {
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dump is the main workhorse for dumping a value. It uses the passed reflect
|
||||
// value to figure out what kind of object we are dealing with and formats it
|
||||
// appropriately. It is a recursive function, however circular data structures
|
||||
// are detected and handled properly.
|
||||
func (d *dumpState) dump(v reflect.Value) {
|
||||
// Handle invalid reflect values immediately.
|
||||
kind := v.Kind()
|
||||
if kind == reflect.Invalid {
|
||||
d.w.Write(invalidAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle pointers specially.
|
||||
if kind == reflect.Ptr {
|
||||
d.indent()
|
||||
d.dumpPtr(v)
|
||||
return
|
||||
}
|
||||
|
||||
// Print type information unless already handled elsewhere.
|
||||
if !d.ignoreNextType {
|
||||
d.indent()
|
||||
d.w.Write(openParenBytes)
|
||||
d.w.Write([]byte(v.Type().String()))
|
||||
d.w.Write(closeParenBytes)
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
d.ignoreNextType = false
|
||||
|
||||
// Display length and capacity if the built-in len and cap functions
|
||||
// work with the value's kind and the len/cap itself is non-zero.
|
||||
valueLen, valueCap := 0, 0
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.Chan:
|
||||
valueLen, valueCap = v.Len(), v.Cap()
|
||||
case reflect.Map, reflect.String:
|
||||
valueLen = v.Len()
|
||||
}
|
||||
if valueLen != 0 || valueCap != 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
if valueLen != 0 {
|
||||
d.w.Write(lenEqualsBytes)
|
||||
printInt(d.w, int64(valueLen), 10)
|
||||
}
|
||||
if valueCap != 0 {
|
||||
if valueLen != 0 {
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
d.w.Write(capEqualsBytes)
|
||||
printInt(d.w, int64(valueCap), 10)
|
||||
}
|
||||
d.w.Write(closeParenBytes)
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
|
||||
// Call Stringer/error interfaces if they exist and the handle methods flag
|
||||
// is enabled
|
||||
if !d.cs.DisableMethods {
|
||||
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||
if handled := handleMethods(d.cs, d.w, v); handled {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Invalid:
|
||||
// Do nothing. We should never get here since invalid has already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Bool:
|
||||
printBool(d.w, v.Bool())
|
||||
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
printInt(d.w, v.Int(), 10)
|
||||
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
printUint(d.w, v.Uint(), 10)
|
||||
|
||||
case reflect.Float32:
|
||||
printFloat(d.w, v.Float(), 32)
|
||||
|
||||
case reflect.Float64:
|
||||
printFloat(d.w, v.Float(), 64)
|
||||
|
||||
case reflect.Complex64:
|
||||
printComplex(d.w, v.Complex(), 32)
|
||||
|
||||
case reflect.Complex128:
|
||||
printComplex(d.w, v.Complex(), 64)
|
||||
|
||||
case reflect.Slice:
|
||||
if v.IsNil() {
|
||||
d.w.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case reflect.Array:
|
||||
d.w.Write(openBraceNewlineBytes)
|
||||
d.depth++
|
||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||
d.indent()
|
||||
d.w.Write(maxNewlineBytes)
|
||||
} else {
|
||||
d.dumpSlice(v)
|
||||
}
|
||||
d.depth--
|
||||
d.indent()
|
||||
d.w.Write(closeBraceBytes)
|
||||
|
||||
case reflect.String:
|
||||
d.w.Write([]byte(strconv.Quote(v.String())))
|
||||
|
||||
case reflect.Interface:
|
||||
// The only time we should get here is for nil interfaces due to
|
||||
// unpackValue calls.
|
||||
if v.IsNil() {
|
||||
d.w.Write(nilAngleBytes)
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
// Do nothing. We should never get here since pointers have already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Map:
|
||||
// nil maps should be indicated as different than empty maps
|
||||
if v.IsNil() {
|
||||
d.w.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
|
||||
d.w.Write(openBraceNewlineBytes)
|
||||
d.depth++
|
||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||
d.indent()
|
||||
d.w.Write(maxNewlineBytes)
|
||||
} else {
|
||||
numEntries := v.Len()
|
||||
keys := v.MapKeys()
|
||||
if d.cs.SortKeys {
|
||||
sortValues(keys, d.cs)
|
||||
}
|
||||
for i, key := range keys {
|
||||
d.dump(d.unpackValue(key))
|
||||
d.w.Write(colonSpaceBytes)
|
||||
d.ignoreNextIndent = true
|
||||
d.dump(d.unpackValue(v.MapIndex(key)))
|
||||
if i < (numEntries - 1) {
|
||||
d.w.Write(commaNewlineBytes)
|
||||
} else {
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
d.depth--
|
||||
d.indent()
|
||||
d.w.Write(closeBraceBytes)
|
||||
|
||||
case reflect.Struct:
|
||||
d.w.Write(openBraceNewlineBytes)
|
||||
d.depth++
|
||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||
d.indent()
|
||||
d.w.Write(maxNewlineBytes)
|
||||
} else {
|
||||
vt := v.Type()
|
||||
numFields := v.NumField()
|
||||
for i := 0; i < numFields; i++ {
|
||||
d.indent()
|
||||
vtf := vt.Field(i)
|
||||
d.w.Write([]byte(vtf.Name))
|
||||
d.w.Write(colonSpaceBytes)
|
||||
d.ignoreNextIndent = true
|
||||
d.dump(d.unpackValue(v.Field(i)))
|
||||
if i < (numFields - 1) {
|
||||
d.w.Write(commaNewlineBytes)
|
||||
} else {
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
d.depth--
|
||||
d.indent()
|
||||
d.w.Write(closeBraceBytes)
|
||||
|
||||
case reflect.Uintptr:
|
||||
printHexPtr(d.w, uintptr(v.Uint()))
|
||||
|
||||
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||
printHexPtr(d.w, v.Pointer())
|
||||
|
||||
// There were not any other types at the time this code was written, but
|
||||
// fall back to letting the default fmt package handle it in case any new
|
||||
// types are added.
|
||||
default:
|
||||
if v.CanInterface() {
|
||||
fmt.Fprintf(d.w, "%v", v.Interface())
|
||||
} else {
|
||||
fmt.Fprintf(d.w, "%v", v.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fdump is a helper function to consolidate the logic from the various public
|
||||
// methods which take varying writers and config states.
|
||||
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
|
||||
for _, arg := range a {
|
||||
if arg == nil {
|
||||
w.Write(interfaceBytes)
|
||||
w.Write(spaceBytes)
|
||||
w.Write(nilAngleBytes)
|
||||
w.Write(newlineBytes)
|
||||
continue
|
||||
}
|
||||
|
||||
d := dumpState{w: w, cs: cs}
|
||||
d.pointers = make(map[uintptr]int)
|
||||
d.dump(reflect.ValueOf(arg))
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||
// exactly the same as Dump.
|
||||
func Fdump(w io.Writer, a ...interface{}) {
|
||||
fdump(&Config, w, a...)
|
||||
}
|
||||
|
||||
// Sdump returns a string with the passed arguments formatted exactly the same
|
||||
// as Dump.
|
||||
func Sdump(a ...interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
fdump(&Config, &buf, a...)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
/*
|
||||
Dump displays the passed parameters to standard out with newlines, customizable
|
||||
indentation, and additional debug information such as complete types and all
|
||||
pointer addresses used to indirect to the final value. It provides the
|
||||
following features over the built-in printing facilities provided by the fmt
|
||||
package:
|
||||
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output
|
||||
|
||||
The configuration options are controlled by an exported package global,
|
||||
spew.Config. See ConfigState for options documentation.
|
||||
|
||||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
||||
get the formatted result as a string.
|
||||
*/
|
||||
func Dump(a ...interface{}) {
|
||||
fdump(&Config, os.Stdout, a...)
|
||||
}
|
419
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
419
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
@ -1,419 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// supportedFlags is a list of all the character flags supported by fmt package.
|
||||
const supportedFlags = "0-+# "
|
||||
|
||||
// formatState implements the fmt.Formatter interface and contains information
|
||||
// about the state of a formatting operation. The NewFormatter function can
|
||||
// be used to get a new Formatter which can be used directly as arguments
|
||||
// in standard fmt package printing calls.
|
||||
type formatState struct {
|
||||
value interface{}
|
||||
fs fmt.State
|
||||
depth int
|
||||
pointers map[uintptr]int
|
||||
ignoreNextType bool
|
||||
cs *ConfigState
|
||||
}
|
||||
|
||||
// buildDefaultFormat recreates the original format string without precision
|
||||
// and width information to pass in to fmt.Sprintf in the case of an
|
||||
// unrecognized type. Unless new types are added to the language, this
|
||||
// function won't ever be called.
|
||||
func (f *formatState) buildDefaultFormat() (format string) {
|
||||
buf := bytes.NewBuffer(percentBytes)
|
||||
|
||||
for _, flag := range supportedFlags {
|
||||
if f.fs.Flag(int(flag)) {
|
||||
buf.WriteRune(flag)
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteRune('v')
|
||||
|
||||
format = buf.String()
|
||||
return format
|
||||
}
|
||||
|
||||
// constructOrigFormat recreates the original format string including precision
|
||||
// and width information to pass along to the standard fmt package. This allows
|
||||
// automatic deferral of all format strings this package doesn't support.
|
||||
func (f *formatState) constructOrigFormat(verb rune) (format string) {
|
||||
buf := bytes.NewBuffer(percentBytes)
|
||||
|
||||
for _, flag := range supportedFlags {
|
||||
if f.fs.Flag(int(flag)) {
|
||||
buf.WriteRune(flag)
|
||||
}
|
||||
}
|
||||
|
||||
if width, ok := f.fs.Width(); ok {
|
||||
buf.WriteString(strconv.Itoa(width))
|
||||
}
|
||||
|
||||
if precision, ok := f.fs.Precision(); ok {
|
||||
buf.Write(precisionBytes)
|
||||
buf.WriteString(strconv.Itoa(precision))
|
||||
}
|
||||
|
||||
buf.WriteRune(verb)
|
||||
|
||||
format = buf.String()
|
||||
return format
|
||||
}
|
||||
|
||||
// unpackValue returns values inside of non-nil interfaces when possible and
|
||||
// ensures that types for values which have been unpacked from an interface
|
||||
// are displayed when the show types flag is also set.
|
||||
// This is useful for data types like structs, arrays, slices, and maps which
|
||||
// can contain varying types packed inside an interface.
|
||||
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
|
||||
if v.Kind() == reflect.Interface {
|
||||
f.ignoreNextType = false
|
||||
if !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// formatPtr handles formatting of pointers by indirecting them as necessary.
|
||||
func (f *formatState) formatPtr(v reflect.Value) {
|
||||
// Display nil if top level pointer is nil.
|
||||
showTypes := f.fs.Flag('#')
|
||||
if v.IsNil() && (!showTypes || f.ignoreNextType) {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove pointers at or below the current depth from map used to detect
|
||||
// circular refs.
|
||||
for k, depth := range f.pointers {
|
||||
if depth >= f.depth {
|
||||
delete(f.pointers, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Keep list of all dereferenced pointers to possibly show later.
|
||||
pointerChain := make([]uintptr, 0)
|
||||
|
||||
// Figure out how many levels of indirection there are by derferencing
|
||||
// pointers and unpacking interfaces down the chain while detecting circular
|
||||
// references.
|
||||
nilFound := false
|
||||
cycleFound := false
|
||||
indirects := 0
|
||||
ve := v
|
||||
for ve.Kind() == reflect.Ptr {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
indirects++
|
||||
addr := ve.Pointer()
|
||||
pointerChain = append(pointerChain, addr)
|
||||
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
|
||||
cycleFound = true
|
||||
indirects--
|
||||
break
|
||||
}
|
||||
f.pointers[addr] = f.depth
|
||||
|
||||
ve = ve.Elem()
|
||||
if ve.Kind() == reflect.Interface {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
ve = ve.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
// Display type or indirection level depending on flags.
|
||||
if showTypes && !f.ignoreNextType {
|
||||
f.fs.Write(openParenBytes)
|
||||
f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||
f.fs.Write([]byte(ve.Type().String()))
|
||||
f.fs.Write(closeParenBytes)
|
||||
} else {
|
||||
if nilFound || cycleFound {
|
||||
indirects += strings.Count(ve.Type().String(), "*")
|
||||
}
|
||||
f.fs.Write(openAngleBytes)
|
||||
f.fs.Write([]byte(strings.Repeat("*", indirects)))
|
||||
f.fs.Write(closeAngleBytes)
|
||||
}
|
||||
|
||||
// Display pointer information depending on flags.
|
||||
if f.fs.Flag('+') && (len(pointerChain) > 0) {
|
||||
f.fs.Write(openParenBytes)
|
||||
for i, addr := range pointerChain {
|
||||
if i > 0 {
|
||||
f.fs.Write(pointerChainBytes)
|
||||
}
|
||||
printHexPtr(f.fs, addr)
|
||||
}
|
||||
f.fs.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// Display dereferenced value.
|
||||
switch {
|
||||
case nilFound == true:
|
||||
f.fs.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound == true:
|
||||
f.fs.Write(circularShortBytes)
|
||||
|
||||
default:
|
||||
f.ignoreNextType = true
|
||||
f.format(ve)
|
||||
}
|
||||
}
|
||||
|
||||
// format is the main workhorse for providing the Formatter interface. It
|
||||
// uses the passed reflect value to figure out what kind of object we are
|
||||
// dealing with and formats it appropriately. It is a recursive function,
|
||||
// however circular data structures are detected and handled properly.
|
||||
func (f *formatState) format(v reflect.Value) {
|
||||
// Handle invalid reflect values immediately.
|
||||
kind := v.Kind()
|
||||
if kind == reflect.Invalid {
|
||||
f.fs.Write(invalidAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle pointers specially.
|
||||
if kind == reflect.Ptr {
|
||||
f.formatPtr(v)
|
||||
return
|
||||
}
|
||||
|
||||
// Print type information unless already handled elsewhere.
|
||||
if !f.ignoreNextType && f.fs.Flag('#') {
|
||||
f.fs.Write(openParenBytes)
|
||||
f.fs.Write([]byte(v.Type().String()))
|
||||
f.fs.Write(closeParenBytes)
|
||||
}
|
||||
f.ignoreNextType = false
|
||||
|
||||
// Call Stringer/error interfaces if they exist and the handle methods
|
||||
// flag is enabled.
|
||||
if !f.cs.DisableMethods {
|
||||
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||
if handled := handleMethods(f.cs, f.fs, v); handled {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Invalid:
|
||||
// Do nothing. We should never get here since invalid has already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Bool:
|
||||
printBool(f.fs, v.Bool())
|
||||
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
printInt(f.fs, v.Int(), 10)
|
||||
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
printUint(f.fs, v.Uint(), 10)
|
||||
|
||||
case reflect.Float32:
|
||||
printFloat(f.fs, v.Float(), 32)
|
||||
|
||||
case reflect.Float64:
|
||||
printFloat(f.fs, v.Float(), 64)
|
||||
|
||||
case reflect.Complex64:
|
||||
printComplex(f.fs, v.Complex(), 32)
|
||||
|
||||
case reflect.Complex128:
|
||||
printComplex(f.fs, v.Complex(), 64)
|
||||
|
||||
case reflect.Slice:
|
||||
if v.IsNil() {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case reflect.Array:
|
||||
f.fs.Write(openBracketBytes)
|
||||
f.depth++
|
||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||
f.fs.Write(maxShortBytes)
|
||||
} else {
|
||||
numEntries := v.Len()
|
||||
for i := 0; i < numEntries; i++ {
|
||||
if i > 0 {
|
||||
f.fs.Write(spaceBytes)
|
||||
}
|
||||
f.ignoreNextType = true
|
||||
f.format(f.unpackValue(v.Index(i)))
|
||||
}
|
||||
}
|
||||
f.depth--
|
||||
f.fs.Write(closeBracketBytes)
|
||||
|
||||
case reflect.String:
|
||||
f.fs.Write([]byte(v.String()))
|
||||
|
||||
case reflect.Interface:
|
||||
// The only time we should get here is for nil interfaces due to
|
||||
// unpackValue calls.
|
||||
if v.IsNil() {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
// Do nothing. We should never get here since pointers have already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Map:
|
||||
// nil maps should be indicated as different than empty maps
|
||||
if v.IsNil() {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
|
||||
f.fs.Write(openMapBytes)
|
||||
f.depth++
|
||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||
f.fs.Write(maxShortBytes)
|
||||
} else {
|
||||
keys := v.MapKeys()
|
||||
if f.cs.SortKeys {
|
||||
sortValues(keys, f.cs)
|
||||
}
|
||||
for i, key := range keys {
|
||||
if i > 0 {
|
||||
f.fs.Write(spaceBytes)
|
||||
}
|
||||
f.ignoreNextType = true
|
||||
f.format(f.unpackValue(key))
|
||||
f.fs.Write(colonBytes)
|
||||
f.ignoreNextType = true
|
||||
f.format(f.unpackValue(v.MapIndex(key)))
|
||||
}
|
||||
}
|
||||
f.depth--
|
||||
f.fs.Write(closeMapBytes)
|
||||
|
||||
case reflect.Struct:
|
||||
numFields := v.NumField()
|
||||
f.fs.Write(openBraceBytes)
|
||||
f.depth++
|
||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||
f.fs.Write(maxShortBytes)
|
||||
} else {
|
||||
vt := v.Type()
|
||||
for i := 0; i < numFields; i++ {
|
||||
if i > 0 {
|
||||
f.fs.Write(spaceBytes)
|
||||
}
|
||||
vtf := vt.Field(i)
|
||||
if f.fs.Flag('+') || f.fs.Flag('#') {
|
||||
f.fs.Write([]byte(vtf.Name))
|
||||
f.fs.Write(colonBytes)
|
||||
}
|
||||
f.format(f.unpackValue(v.Field(i)))
|
||||
}
|
||||
}
|
||||
f.depth--
|
||||
f.fs.Write(closeBraceBytes)
|
||||
|
||||
case reflect.Uintptr:
|
||||
printHexPtr(f.fs, uintptr(v.Uint()))
|
||||
|
||||
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||
printHexPtr(f.fs, v.Pointer())
|
||||
|
||||
// There were not any other types at the time this code was written, but
|
||||
// fall back to letting the default fmt package handle it if any get added.
|
||||
default:
|
||||
format := f.buildDefaultFormat()
|
||||
if v.CanInterface() {
|
||||
fmt.Fprintf(f.fs, format, v.Interface())
|
||||
} else {
|
||||
fmt.Fprintf(f.fs, format, v.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
|
||||
// details.
|
||||
func (f *formatState) Format(fs fmt.State, verb rune) {
|
||||
f.fs = fs
|
||||
|
||||
// Use standard formatting for verbs that are not v.
|
||||
if verb != 'v' {
|
||||
format := f.constructOrigFormat(verb)
|
||||
fmt.Fprintf(fs, format, f.value)
|
||||
return
|
||||
}
|
||||
|
||||
if f.value == nil {
|
||||
if fs.Flag('#') {
|
||||
fs.Write(interfaceBytes)
|
||||
}
|
||||
fs.Write(nilAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
f.format(reflect.ValueOf(f.value))
|
||||
}
|
||||
|
||||
// newFormatter is a helper function to consolidate the logic from the various
|
||||
// public methods which take varying config states.
|
||||
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
|
||||
fs := &formatState{value: v, cs: cs}
|
||||
fs.pointers = make(map[uintptr]int)
|
||||
return fs
|
||||
}
|
||||
|
||||
/*
|
||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||
interface. As a result, it integrates cleanly with standard fmt package
|
||||
printing functions. The formatter is useful for inline printing of smaller data
|
||||
types similar to the standard %v format specifier.
|
||||
|
||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||
the width and precision arguments (however they will still work on the format
|
||||
specifiers not handled by the custom formatter).
|
||||
|
||||
Typically this function shouldn't be called directly. It is much easier to make
|
||||
use of the custom formatter by calling one of the convenience functions such as
|
||||
Printf, Println, or Fprintf.
|
||||
*/
|
||||
func NewFormatter(v interface{}) fmt.Formatter {
|
||||
return newFormatter(&Config, v)
|
||||
}
|
148
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
148
vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the formatted string as a value that satisfies error. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Errorf(format string, a ...interface{}) (err error) {
|
||||
return fmt.Errorf(format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprint(w, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintf(w, format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
||||
// passed with a default Formatter interface returned by NewFormatter. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(w, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Print(a ...interface{}) (n int, err error) {
|
||||
return fmt.Print(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Printf(format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Printf(format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Println(a ...interface{}) (n int, err error) {
|
||||
return fmt.Println(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Sprint(a ...interface{}) string {
|
||||
return fmt.Sprint(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Sprintf(format string, a ...interface{}) string {
|
||||
return fmt.Sprintf(format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
||||
// were passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Sprintln(a ...interface{}) string {
|
||||
return fmt.Sprintln(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// convertArgs accepts a slice of arguments and returns a slice of the same
|
||||
// length with each argument converted to a default spew Formatter interface.
|
||||
func convertArgs(args []interface{}) (formatters []interface{}) {
|
||||
formatters = make([]interface{}, len(args))
|
||||
for index, arg := range args {
|
||||
formatters[index] = NewFormatter(arg)
|
||||
}
|
||||
return formatters
|
||||
}
|
27
vendor/github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/LICENSE
generated
vendored
27
vendor/github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/LICENSE
generated
vendored
@ -1,27 +0,0 @@
|
||||
Copyright (c) 2013, Patrick Mezard
|
||||
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.
|
||||
The names of its contributors may not 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
|
||||
HOLDER 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.
|
758
vendor/github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib/difflib.go
generated
vendored
758
vendor/github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib/difflib.go
generated
vendored
@ -1,758 +0,0 @@
|
||||
// Package difflib is a partial port of Python difflib module.
|
||||
//
|
||||
// It provides tools to compare sequences of strings and generate textual diffs.
|
||||
//
|
||||
// The following class and functions have been ported:
|
||||
//
|
||||
// - SequenceMatcher
|
||||
//
|
||||
// - unified_diff
|
||||
//
|
||||
// - context_diff
|
||||
//
|
||||
// Getting unified diffs was the main goal of the port. Keep in mind this code
|
||||
// is mostly suitable to output text differences in a human friendly way, there
|
||||
// are no guarantees generated diffs are consumable by patch(1).
|
||||
package difflib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func calculateRatio(matches, length int) float64 {
|
||||
if length > 0 {
|
||||
return 2.0 * float64(matches) / float64(length)
|
||||
}
|
||||
return 1.0
|
||||
}
|
||||
|
||||
type Match struct {
|
||||
A int
|
||||
B int
|
||||
Size int
|
||||
}
|
||||
|
||||
type OpCode struct {
|
||||
Tag byte
|
||||
I1 int
|
||||
I2 int
|
||||
J1 int
|
||||
J2 int
|
||||
}
|
||||
|
||||
// SequenceMatcher compares sequence of strings. The basic
|
||||
// algorithm predates, and is a little fancier than, an algorithm
|
||||
// published in the late 1980's by Ratcliff and Obershelp under the
|
||||
// hyperbolic name "gestalt pattern matching". The basic idea is to find
|
||||
// the longest contiguous matching subsequence that contains no "junk"
|
||||
// elements (R-O doesn't address junk). The same idea is then applied
|
||||
// recursively to the pieces of the sequences to the left and to the right
|
||||
// of the matching subsequence. This does not yield minimal edit
|
||||
// sequences, but does tend to yield matches that "look right" to people.
|
||||
//
|
||||
// SequenceMatcher tries to compute a "human-friendly diff" between two
|
||||
// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the
|
||||
// longest *contiguous* & junk-free matching subsequence. That's what
|
||||
// catches peoples' eyes. The Windows(tm) windiff has another interesting
|
||||
// notion, pairing up elements that appear uniquely in each sequence.
|
||||
// That, and the method here, appear to yield more intuitive difference
|
||||
// reports than does diff. This method appears to be the least vulnerable
|
||||
// to synching up on blocks of "junk lines", though (like blank lines in
|
||||
// ordinary text files, or maybe "<P>" lines in HTML files). That may be
|
||||
// because this is the only method of the 3 that has a *concept* of
|
||||
// "junk" <wink>.
|
||||
//
|
||||
// Timing: Basic R-O is cubic time worst case and quadratic time expected
|
||||
// case. SequenceMatcher is quadratic time for the worst case and has
|
||||
// expected-case behavior dependent in a complicated way on how many
|
||||
// elements the sequences have in common; best case time is linear.
|
||||
type SequenceMatcher struct {
|
||||
a []string
|
||||
b []string
|
||||
b2j map[string][]int
|
||||
IsJunk func(string) bool
|
||||
autoJunk bool
|
||||
bJunk map[string]struct{}
|
||||
matchingBlocks []Match
|
||||
fullBCount map[string]int
|
||||
bPopular map[string]struct{}
|
||||
opCodes []OpCode
|
||||
}
|
||||
|
||||
func NewMatcher(a, b []string) *SequenceMatcher {
|
||||
m := SequenceMatcher{autoJunk: true}
|
||||
m.SetSeqs(a, b)
|
||||
return &m
|
||||
}
|
||||
|
||||
func NewMatcherWithJunk(a, b []string, autoJunk bool,
|
||||
isJunk func(string) bool) *SequenceMatcher {
|
||||
|
||||
m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
|
||||
m.SetSeqs(a, b)
|
||||
return &m
|
||||
}
|
||||
|
||||
// Set two sequences to be compared.
|
||||
func (m *SequenceMatcher) SetSeqs(a, b []string) {
|
||||
m.SetSeq1(a)
|
||||
m.SetSeq2(b)
|
||||
}
|
||||
|
||||
// Set the first sequence to be compared. The second sequence to be compared is
|
||||
// not changed.
|
||||
//
|
||||
// SequenceMatcher computes and caches detailed information about the second
|
||||
// sequence, so if you want to compare one sequence S against many sequences,
|
||||
// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
|
||||
// sequences.
|
||||
//
|
||||
// See also SetSeqs() and SetSeq2().
|
||||
func (m *SequenceMatcher) SetSeq1(a []string) {
|
||||
if &a == &m.a {
|
||||
return
|
||||
}
|
||||
m.a = a
|
||||
m.matchingBlocks = nil
|
||||
m.opCodes = nil
|
||||
}
|
||||
|
||||
// Set the second sequence to be compared. The first sequence to be compared is
|
||||
// not changed.
|
||||
func (m *SequenceMatcher) SetSeq2(b []string) {
|
||||
if &b == &m.b {
|
||||
return
|
||||
}
|
||||
m.b = b
|
||||
m.matchingBlocks = nil
|
||||
m.opCodes = nil
|
||||
m.fullBCount = nil
|
||||
m.chainB()
|
||||
}
|
||||
|
||||
func (m *SequenceMatcher) chainB() {
|
||||
// Populate line -> index mapping
|
||||
b2j := map[string][]int{}
|
||||
for i, s := range m.b {
|
||||
indices := b2j[s]
|
||||
indices = append(indices, i)
|
||||
b2j[s] = indices
|
||||
}
|
||||
|
||||
// Purge junk elements
|
||||
m.bJunk = map[string]struct{}{}
|
||||
if m.IsJunk != nil {
|
||||
junk := m.bJunk
|
||||
for s, _ := range b2j {
|
||||
if m.IsJunk(s) {
|
||||
junk[s] = struct{}{}
|
||||
}
|
||||
}
|
||||
for s, _ := range junk {
|
||||
delete(b2j, s)
|
||||
}
|
||||
}
|
||||
|
||||
// Purge remaining popular elements
|
||||
popular := map[string]struct{}{}
|
||||
n := len(m.b)
|
||||
if m.autoJunk && n >= 200 {
|
||||
ntest := n/100 + 1
|
||||
for s, indices := range b2j {
|
||||
if len(indices) > ntest {
|
||||
popular[s] = struct{}{}
|
||||
}
|
||||
}
|
||||
for s, _ := range popular {
|
||||
delete(b2j, s)
|
||||
}
|
||||
}
|
||||
m.bPopular = popular
|
||||
m.b2j = b2j
|
||||
}
|
||||
|
||||
func (m *SequenceMatcher) isBJunk(s string) bool {
|
||||
_, ok := m.bJunk[s]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Find longest matching block in a[alo:ahi] and b[blo:bhi].
|
||||
//
|
||||
// If IsJunk is not defined:
|
||||
//
|
||||
// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
|
||||
// alo <= i <= i+k <= ahi
|
||||
// blo <= j <= j+k <= bhi
|
||||
// and for all (i',j',k') meeting those conditions,
|
||||
// k >= k'
|
||||
// i <= i'
|
||||
// and if i == i', j <= j'
|
||||
//
|
||||
// In other words, of all maximal matching blocks, return one that
|
||||
// starts earliest in a, and of all those maximal matching blocks that
|
||||
// start earliest in a, return the one that starts earliest in b.
|
||||
//
|
||||
// If IsJunk is defined, first the longest matching block is
|
||||
// determined as above, but with the additional restriction that no
|
||||
// junk element appears in the block. Then that block is extended as
|
||||
// far as possible by matching (only) junk elements on both sides. So
|
||||
// the resulting block never matches on junk except as identical junk
|
||||
// happens to be adjacent to an "interesting" match.
|
||||
//
|
||||
// If no blocks match, return (alo, blo, 0).
|
||||
func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
|
||||
// CAUTION: stripping common prefix or suffix would be incorrect.
|
||||
// E.g.,
|
||||
// ab
|
||||
// acab
|
||||
// Longest matching block is "ab", but if common prefix is
|
||||
// stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
|
||||
// strip, so ends up claiming that ab is changed to acab by
|
||||
// inserting "ca" in the middle. That's minimal but unintuitive:
|
||||
// "it's obvious" that someone inserted "ac" at the front.
|
||||
// Windiff ends up at the same place as diff, but by pairing up
|
||||
// the unique 'b's and then matching the first two 'a's.
|
||||
besti, bestj, bestsize := alo, blo, 0
|
||||
|
||||
// find longest junk-free match
|
||||
// during an iteration of the loop, j2len[j] = length of longest
|
||||
// junk-free match ending with a[i-1] and b[j]
|
||||
j2len := map[int]int{}
|
||||
for i := alo; i != ahi; i++ {
|
||||
// look at all instances of a[i] in b; note that because
|
||||
// b2j has no junk keys, the loop is skipped if a[i] is junk
|
||||
newj2len := map[int]int{}
|
||||
for _, j := range m.b2j[m.a[i]] {
|
||||
// a[i] matches b[j]
|
||||
if j < blo {
|
||||
continue
|
||||
}
|
||||
if j >= bhi {
|
||||
break
|
||||
}
|
||||
k := j2len[j-1] + 1
|
||||
newj2len[j] = k
|
||||
if k > bestsize {
|
||||
besti, bestj, bestsize = i-k+1, j-k+1, k
|
||||
}
|
||||
}
|
||||
j2len = newj2len
|
||||
}
|
||||
|
||||
// Extend the best by non-junk elements on each end. In particular,
|
||||
// "popular" non-junk elements aren't in b2j, which greatly speeds
|
||||
// the inner loop above, but also means "the best" match so far
|
||||
// doesn't contain any junk *or* popular non-junk elements.
|
||||
for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
|
||||
m.a[besti-1] == m.b[bestj-1] {
|
||||
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
|
||||
}
|
||||
for besti+bestsize < ahi && bestj+bestsize < bhi &&
|
||||
!m.isBJunk(m.b[bestj+bestsize]) &&
|
||||
m.a[besti+bestsize] == m.b[bestj+bestsize] {
|
||||
bestsize += 1
|
||||
}
|
||||
|
||||
// Now that we have a wholly interesting match (albeit possibly
|
||||
// empty!), we may as well suck up the matching junk on each
|
||||
// side of it too. Can't think of a good reason not to, and it
|
||||
// saves post-processing the (possibly considerable) expense of
|
||||
// figuring out what to do with it. In the case of an empty
|
||||
// interesting match, this is clearly the right thing to do,
|
||||
// because no other kind of match is possible in the regions.
|
||||
for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
|
||||
m.a[besti-1] == m.b[bestj-1] {
|
||||
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
|
||||
}
|
||||
for besti+bestsize < ahi && bestj+bestsize < bhi &&
|
||||
m.isBJunk(m.b[bestj+bestsize]) &&
|
||||
m.a[besti+bestsize] == m.b[bestj+bestsize] {
|
||||
bestsize += 1
|
||||
}
|
||||
|
||||
return Match{A: besti, B: bestj, Size: bestsize}
|
||||
}
|
||||
|
||||
// Return list of triples describing matching subsequences.
|
||||
//
|
||||
// Each triple is of the form (i, j, n), and means that
|
||||
// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in
|
||||
// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
|
||||
// adjacent triples in the list, and the second is not the last triple in the
|
||||
// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
|
||||
// adjacent equal blocks.
|
||||
//
|
||||
// The last triple is a dummy, (len(a), len(b), 0), and is the only
|
||||
// triple with n==0.
|
||||
func (m *SequenceMatcher) GetMatchingBlocks() []Match {
|
||||
if m.matchingBlocks != nil {
|
||||
return m.matchingBlocks
|
||||
}
|
||||
|
||||
var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
|
||||
matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
|
||||
match := m.findLongestMatch(alo, ahi, blo, bhi)
|
||||
i, j, k := match.A, match.B, match.Size
|
||||
if match.Size > 0 {
|
||||
if alo < i && blo < j {
|
||||
matched = matchBlocks(alo, i, blo, j, matched)
|
||||
}
|
||||
matched = append(matched, match)
|
||||
if i+k < ahi && j+k < bhi {
|
||||
matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
|
||||
}
|
||||
}
|
||||
return matched
|
||||
}
|
||||
matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
|
||||
|
||||
// It's possible that we have adjacent equal blocks in the
|
||||
// matching_blocks list now.
|
||||
nonAdjacent := []Match{}
|
||||
i1, j1, k1 := 0, 0, 0
|
||||
for _, b := range matched {
|
||||
// Is this block adjacent to i1, j1, k1?
|
||||
i2, j2, k2 := b.A, b.B, b.Size
|
||||
if i1+k1 == i2 && j1+k1 == j2 {
|
||||
// Yes, so collapse them -- this just increases the length of
|
||||
// the first block by the length of the second, and the first
|
||||
// block so lengthened remains the block to compare against.
|
||||
k1 += k2
|
||||
} else {
|
||||
// Not adjacent. Remember the first block (k1==0 means it's
|
||||
// the dummy we started with), and make the second block the
|
||||
// new block to compare against.
|
||||
if k1 > 0 {
|
||||
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
|
||||
}
|
||||
i1, j1, k1 = i2, j2, k2
|
||||
}
|
||||
}
|
||||
if k1 > 0 {
|
||||
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
|
||||
}
|
||||
|
||||
nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
|
||||
m.matchingBlocks = nonAdjacent
|
||||
return m.matchingBlocks
|
||||
}
|
||||
|
||||
// Return list of 5-tuples describing how to turn a into b.
|
||||
//
|
||||
// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple
|
||||
// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
|
||||
// tuple preceding it, and likewise for j1 == the previous j2.
|
||||
//
|
||||
// The tags are characters, with these meanings:
|
||||
//
|
||||
// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2]
|
||||
//
|
||||
// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case.
|
||||
//
|
||||
// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
|
||||
//
|
||||
// 'e' (equal): a[i1:i2] == b[j1:j2]
|
||||
func (m *SequenceMatcher) GetOpCodes() []OpCode {
|
||||
if m.opCodes != nil {
|
||||
return m.opCodes
|
||||
}
|
||||
i, j := 0, 0
|
||||
matching := m.GetMatchingBlocks()
|
||||
opCodes := make([]OpCode, 0, len(matching))
|
||||
for _, m := range matching {
|
||||
// invariant: we've pumped out correct diffs to change
|
||||
// a[:i] into b[:j], and the next matching block is
|
||||
// a[ai:ai+size] == b[bj:bj+size]. So we need to pump
|
||||
// out a diff to change a[i:ai] into b[j:bj], pump out
|
||||
// the matching block, and move (i,j) beyond the match
|
||||
ai, bj, size := m.A, m.B, m.Size
|
||||
tag := byte(0)
|
||||
if i < ai && j < bj {
|
||||
tag = 'r'
|
||||
} else if i < ai {
|
||||
tag = 'd'
|
||||
} else if j < bj {
|
||||
tag = 'i'
|
||||
}
|
||||
if tag > 0 {
|
||||
opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
|
||||
}
|
||||
i, j = ai+size, bj+size
|
||||
// the list of matching blocks is terminated by a
|
||||
// sentinel with size 0
|
||||
if size > 0 {
|
||||
opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
|
||||
}
|
||||
}
|
||||
m.opCodes = opCodes
|
||||
return m.opCodes
|
||||
}
|
||||
|
||||
// Isolate change clusters by eliminating ranges with no changes.
|
||||
//
|
||||
// Return a generator of groups with up to n lines of context.
|
||||
// Each group is in the same format as returned by GetOpCodes().
|
||||
func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
|
||||
if n < 0 {
|
||||
n = 3
|
||||
}
|
||||
codes := m.GetOpCodes()
|
||||
if len(codes) == 0 {
|
||||
codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
|
||||
}
|
||||
// Fixup leading and trailing groups if they show no changes.
|
||||
if codes[0].Tag == 'e' {
|
||||
c := codes[0]
|
||||
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||
codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
|
||||
}
|
||||
if codes[len(codes)-1].Tag == 'e' {
|
||||
c := codes[len(codes)-1]
|
||||
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||
codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
|
||||
}
|
||||
nn := n + n
|
||||
groups := [][]OpCode{}
|
||||
group := []OpCode{}
|
||||
for _, c := range codes {
|
||||
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||
// End the current group and start a new one whenever
|
||||
// there is a large range with no changes.
|
||||
if c.Tag == 'e' && i2-i1 > nn {
|
||||
group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
|
||||
j1, min(j2, j1+n)})
|
||||
groups = append(groups, group)
|
||||
group = []OpCode{}
|
||||
i1, j1 = max(i1, i2-n), max(j1, j2-n)
|
||||
}
|
||||
group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
|
||||
}
|
||||
if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
|
||||
groups = append(groups, group)
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
// Return a measure of the sequences' similarity (float in [0,1]).
|
||||
//
|
||||
// Where T is the total number of elements in both sequences, and
|
||||
// M is the number of matches, this is 2.0*M / T.
|
||||
// Note that this is 1 if the sequences are identical, and 0 if
|
||||
// they have nothing in common.
|
||||
//
|
||||
// .Ratio() is expensive to compute if you haven't already computed
|
||||
// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
|
||||
// want to try .QuickRatio() or .RealQuickRation() first to get an
|
||||
// upper bound.
|
||||
func (m *SequenceMatcher) Ratio() float64 {
|
||||
matches := 0
|
||||
for _, m := range m.GetMatchingBlocks() {
|
||||
matches += m.Size
|
||||
}
|
||||
return calculateRatio(matches, len(m.a)+len(m.b))
|
||||
}
|
||||
|
||||
// Return an upper bound on ratio() relatively quickly.
|
||||
//
|
||||
// This isn't defined beyond that it is an upper bound on .Ratio(), and
|
||||
// is faster to compute.
|
||||
func (m *SequenceMatcher) QuickRatio() float64 {
|
||||
// viewing a and b as multisets, set matches to the cardinality
|
||||
// of their intersection; this counts the number of matches
|
||||
// without regard to order, so is clearly an upper bound
|
||||
if m.fullBCount == nil {
|
||||
m.fullBCount = map[string]int{}
|
||||
for _, s := range m.b {
|
||||
m.fullBCount[s] = m.fullBCount[s] + 1
|
||||
}
|
||||
}
|
||||
|
||||
// avail[x] is the number of times x appears in 'b' less the
|
||||
// number of times we've seen it in 'a' so far ... kinda
|
||||
avail := map[string]int{}
|
||||
matches := 0
|
||||
for _, s := range m.a {
|
||||
n, ok := avail[s]
|
||||
if !ok {
|
||||
n = m.fullBCount[s]
|
||||
}
|
||||
avail[s] = n - 1
|
||||
if n > 0 {
|
||||
matches += 1
|
||||
}
|
||||
}
|
||||
return calculateRatio(matches, len(m.a)+len(m.b))
|
||||
}
|
||||
|
||||
// Return an upper bound on ratio() very quickly.
|
||||
//
|
||||
// This isn't defined beyond that it is an upper bound on .Ratio(), and
|
||||
// is faster to compute than either .Ratio() or .QuickRatio().
|
||||
func (m *SequenceMatcher) RealQuickRatio() float64 {
|
||||
la, lb := len(m.a), len(m.b)
|
||||
return calculateRatio(min(la, lb), la+lb)
|
||||
}
|
||||
|
||||
// Convert range to the "ed" format
|
||||
func formatRangeUnified(start, stop int) string {
|
||||
// Per the diff spec at http://www.unix.org/single_unix_specification/
|
||||
beginning := start + 1 // lines start numbering with one
|
||||
length := stop - start
|
||||
if length == 1 {
|
||||
return fmt.Sprintf("%d", beginning)
|
||||
}
|
||||
if length == 0 {
|
||||
beginning -= 1 // empty ranges begin at line just before the range
|
||||
}
|
||||
return fmt.Sprintf("%d,%d", beginning, length)
|
||||
}
|
||||
|
||||
// Unified diff parameters
|
||||
type UnifiedDiff struct {
|
||||
A []string // First sequence lines
|
||||
FromFile string // First file name
|
||||
FromDate string // First file time
|
||||
B []string // Second sequence lines
|
||||
ToFile string // Second file name
|
||||
ToDate string // Second file time
|
||||
Eol string // Headers end of line, defaults to LF
|
||||
Context int // Number of context lines
|
||||
}
|
||||
|
||||
// Compare two sequences of lines; generate the delta as a unified diff.
|
||||
//
|
||||
// Unified diffs are a compact way of showing line changes and a few
|
||||
// lines of context. The number of context lines is set by 'n' which
|
||||
// defaults to three.
|
||||
//
|
||||
// By default, the diff control lines (those with ---, +++, or @@) are
|
||||
// created with a trailing newline. This is helpful so that inputs
|
||||
// created from file.readlines() result in diffs that are suitable for
|
||||
// file.writelines() since both the inputs and outputs have trailing
|
||||
// newlines.
|
||||
//
|
||||
// For inputs that do not have trailing newlines, set the lineterm
|
||||
// argument to "" so that the output will be uniformly newline free.
|
||||
//
|
||||
// The unidiff format normally has a header for filenames and modification
|
||||
// times. Any or all of these may be specified using strings for
|
||||
// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
|
||||
// The modification times are normally expressed in the ISO 8601 format.
|
||||
func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
|
||||
buf := bufio.NewWriter(writer)
|
||||
defer buf.Flush()
|
||||
w := func(format string, args ...interface{}) error {
|
||||
_, err := buf.WriteString(fmt.Sprintf(format, args...))
|
||||
return err
|
||||
}
|
||||
|
||||
if len(diff.Eol) == 0 {
|
||||
diff.Eol = "\n"
|
||||
}
|
||||
|
||||
started := false
|
||||
m := NewMatcher(diff.A, diff.B)
|
||||
for _, g := range m.GetGroupedOpCodes(diff.Context) {
|
||||
if !started {
|
||||
started = true
|
||||
fromDate := ""
|
||||
if len(diff.FromDate) > 0 {
|
||||
fromDate = "\t" + diff.FromDate
|
||||
}
|
||||
toDate := ""
|
||||
if len(diff.ToDate) > 0 {
|
||||
toDate = "\t" + diff.ToDate
|
||||
}
|
||||
err := w("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = w("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
first, last := g[0], g[len(g)-1]
|
||||
range1 := formatRangeUnified(first.I1, last.I2)
|
||||
range2 := formatRangeUnified(first.J1, last.J2)
|
||||
if err := w("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, c := range g {
|
||||
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||
if c.Tag == 'e' {
|
||||
for _, line := range diff.A[i1:i2] {
|
||||
if err := w(" " + line); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if c.Tag == 'r' || c.Tag == 'd' {
|
||||
for _, line := range diff.A[i1:i2] {
|
||||
if err := w("-" + line); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.Tag == 'r' || c.Tag == 'i' {
|
||||
for _, line := range diff.B[j1:j2] {
|
||||
if err := w("+" + line); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Like WriteUnifiedDiff but returns the diff a string.
|
||||
func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
|
||||
w := &bytes.Buffer{}
|
||||
err := WriteUnifiedDiff(w, diff)
|
||||
return string(w.Bytes()), err
|
||||
}
|
||||
|
||||
// Convert range to the "ed" format.
|
||||
func formatRangeContext(start, stop int) string {
|
||||
// Per the diff spec at http://www.unix.org/single_unix_specification/
|
||||
beginning := start + 1 // lines start numbering with one
|
||||
length := stop - start
|
||||
if length == 0 {
|
||||
beginning -= 1 // empty ranges begin at line just before the range
|
||||
}
|
||||
if length <= 1 {
|
||||
return fmt.Sprintf("%d", beginning)
|
||||
}
|
||||
return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
|
||||
}
|
||||
|
||||
type ContextDiff UnifiedDiff
|
||||
|
||||
// Compare two sequences of lines; generate the delta as a context diff.
|
||||
//
|
||||
// Context diffs are a compact way of showing line changes and a few
|
||||
// lines of context. The number of context lines is set by diff.Context
|
||||
// which defaults to three.
|
||||
//
|
||||
// By default, the diff control lines (those with *** or ---) are
|
||||
// created with a trailing newline.
|
||||
//
|
||||
// For inputs that do not have trailing newlines, set the diff.Eol
|
||||
// argument to "" so that the output will be uniformly newline free.
|
||||
//
|
||||
// The context diff format normally has a header for filenames and
|
||||
// modification times. Any or all of these may be specified using
|
||||
// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
|
||||
// The modification times are normally expressed in the ISO 8601 format.
|
||||
// If not specified, the strings default to blanks.
|
||||
func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
|
||||
buf := bufio.NewWriter(writer)
|
||||
defer buf.Flush()
|
||||
var diffErr error
|
||||
w := func(format string, args ...interface{}) {
|
||||
_, err := buf.WriteString(fmt.Sprintf(format, args...))
|
||||
if diffErr == nil && err != nil {
|
||||
diffErr = err
|
||||
}
|
||||
}
|
||||
|
||||
if len(diff.Eol) == 0 {
|
||||
diff.Eol = "\n"
|
||||
}
|
||||
|
||||
prefix := map[byte]string{
|
||||
'i': "+ ",
|
||||
'd': "- ",
|
||||
'r': "! ",
|
||||
'e': " ",
|
||||
}
|
||||
|
||||
started := false
|
||||
m := NewMatcher(diff.A, diff.B)
|
||||
for _, g := range m.GetGroupedOpCodes(diff.Context) {
|
||||
if !started {
|
||||
started = true
|
||||
fromDate := ""
|
||||
if len(diff.FromDate) > 0 {
|
||||
fromDate = "\t" + diff.FromDate
|
||||
}
|
||||
toDate := ""
|
||||
if len(diff.ToDate) > 0 {
|
||||
toDate = "\t" + diff.ToDate
|
||||
}
|
||||
w("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
|
||||
w("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
|
||||
}
|
||||
|
||||
first, last := g[0], g[len(g)-1]
|
||||
w("***************" + diff.Eol)
|
||||
|
||||
range1 := formatRangeContext(first.I1, last.I2)
|
||||
w("*** %s ****%s", range1, diff.Eol)
|
||||
for _, c := range g {
|
||||
if c.Tag == 'r' || c.Tag == 'd' {
|
||||
for _, cc := range g {
|
||||
if cc.Tag == 'i' {
|
||||
continue
|
||||
}
|
||||
for _, line := range diff.A[cc.I1:cc.I2] {
|
||||
w(prefix[cc.Tag] + line)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
range2 := formatRangeContext(first.J1, last.J2)
|
||||
w("--- %s ----%s", range2, diff.Eol)
|
||||
for _, c := range g {
|
||||
if c.Tag == 'r' || c.Tag == 'i' {
|
||||
for _, cc := range g {
|
||||
if cc.Tag == 'd' {
|
||||
continue
|
||||
}
|
||||
for _, line := range diff.B[cc.J1:cc.J2] {
|
||||
w(prefix[cc.Tag] + line)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return diffErr
|
||||
}
|
||||
|
||||
// Like WriteContextDiff but returns the diff a string.
|
||||
func GetContextDiffString(diff ContextDiff) (string, error) {
|
||||
w := &bytes.Buffer{}
|
||||
err := WriteContextDiff(w, diff)
|
||||
return string(w.Bytes()), err
|
||||
}
|
||||
|
||||
// Split a string on "\n" while preserving them. The output can be used
|
||||
// as input for UnifiedDiff and ContextDiff structures.
|
||||
func SplitLines(s string) []string {
|
||||
lines := strings.SplitAfter(s, "\n")
|
||||
lines[len(lines)-1] += "\n"
|
||||
return lines
|
||||
}
|
22
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/.gitignore
generated
vendored
22
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/.gitignore
generated
vendored
@ -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
|
23
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/LICENSE.md
generated
vendored
23
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/LICENSE.md
generated
vendored
@ -1,23 +0,0 @@
|
||||
objx - by Mat Ryer and Tyler Bunnell
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Stretchr, 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.
|
3
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/README.md
generated
vendored
3
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/README.md
generated
vendored
@ -1,3 +0,0 @@
|
||||
# objx
|
||||
|
||||
* Jump into the [API Documentation](http://godoc.org/github.com/stretchr/objx)
|
179
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/accessors.go
generated
vendored
179
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/accessors.go
generated
vendored
@ -1,179 +0,0 @@
|
||||
package objx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// arrayAccesRegexString is the regex used to extract the array number
|
||||
// from the access path
|
||||
const arrayAccesRegexString = `^(.+)\[([0-9]+)\]$`
|
||||
|
||||
// arrayAccesRegex is the compiled arrayAccesRegexString
|
||||
var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString)
|
||||
|
||||
// Get gets the value using the specified selector and
|
||||
// returns it inside a new Obj object.
|
||||
//
|
||||
// If it cannot find the value, Get will return a nil
|
||||
// value inside an instance of Obj.
|
||||
//
|
||||
// Get can only operate directly on map[string]interface{} and []interface.
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// To access the title of the third chapter of the second book, do:
|
||||
//
|
||||
// o.Get("books[1].chapters[2].title")
|
||||
func (m Map) Get(selector string) *Value {
|
||||
rawObj := access(m, selector, nil, false, false)
|
||||
return &Value{data: rawObj}
|
||||
}
|
||||
|
||||
// Set sets the value using the specified selector and
|
||||
// returns the object on which Set was called.
|
||||
//
|
||||
// Set can only operate directly on map[string]interface{} and []interface
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// To set the title of the third chapter of the second book, do:
|
||||
//
|
||||
// o.Set("books[1].chapters[2].title","Time to Go")
|
||||
func (m Map) Set(selector string, value interface{}) Map {
|
||||
access(m, selector, value, true, false)
|
||||
return m
|
||||
}
|
||||
|
||||
// access accesses the object using the selector and performs the
|
||||
// appropriate action.
|
||||
func access(current, selector, value interface{}, isSet, panics bool) interface{} {
|
||||
|
||||
switch selector.(type) {
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
|
||||
if array, ok := current.([]interface{}); ok {
|
||||
index := intFromInterface(selector)
|
||||
|
||||
if index >= len(array) {
|
||||
if panics {
|
||||
panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return array[index]
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case string:
|
||||
|
||||
selStr := selector.(string)
|
||||
selSegs := strings.SplitN(selStr, PathSeparator, 2)
|
||||
thisSel := selSegs[0]
|
||||
index := -1
|
||||
var err error
|
||||
|
||||
// https://github.com/stretchr/objx/issues/12
|
||||
if strings.Contains(thisSel, "[") {
|
||||
|
||||
arrayMatches := arrayAccesRegex.FindStringSubmatch(thisSel)
|
||||
|
||||
if len(arrayMatches) > 0 {
|
||||
|
||||
// Get the key into the map
|
||||
thisSel = arrayMatches[1]
|
||||
|
||||
// Get the index into the array at the key
|
||||
index, err = strconv.Atoi(arrayMatches[2])
|
||||
|
||||
if err != nil {
|
||||
// This should never happen. If it does, something has gone
|
||||
// seriously wrong. Panic.
|
||||
panic("objx: Array index is not an integer. Must use array[int].")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if curMap, ok := current.(Map); ok {
|
||||
current = map[string]interface{}(curMap)
|
||||
}
|
||||
|
||||
// get the object in question
|
||||
switch current.(type) {
|
||||
case map[string]interface{}:
|
||||
curMSI := current.(map[string]interface{})
|
||||
if len(selSegs) <= 1 && isSet {
|
||||
curMSI[thisSel] = value
|
||||
return nil
|
||||
} else {
|
||||
current = curMSI[thisSel]
|
||||
}
|
||||
default:
|
||||
current = nil
|
||||
}
|
||||
|
||||
if current == nil && panics {
|
||||
panic(fmt.Sprintf("objx: '%v' invalid on object.", selector))
|
||||
}
|
||||
|
||||
// do we need to access the item of an array?
|
||||
if index > -1 {
|
||||
if array, ok := current.([]interface{}); ok {
|
||||
if index < len(array) {
|
||||
current = array[index]
|
||||
} else {
|
||||
if panics {
|
||||
panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array)))
|
||||
}
|
||||
current = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(selSegs) > 1 {
|
||||
current = access(current, selSegs[1], value, isSet, panics)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return current
|
||||
|
||||
}
|
||||
|
||||
// intFromInterface converts an interface object to the largest
|
||||
// representation of an unsigned integer using a type switch and
|
||||
// assertions
|
||||
func intFromInterface(selector interface{}) int {
|
||||
var value int
|
||||
switch selector.(type) {
|
||||
case int:
|
||||
value = selector.(int)
|
||||
case int8:
|
||||
value = int(selector.(int8))
|
||||
case int16:
|
||||
value = int(selector.(int16))
|
||||
case int32:
|
||||
value = int(selector.(int32))
|
||||
case int64:
|
||||
value = int(selector.(int64))
|
||||
case uint:
|
||||
value = int(selector.(uint))
|
||||
case uint8:
|
||||
value = int(selector.(uint8))
|
||||
case uint16:
|
||||
value = int(selector.(uint16))
|
||||
case uint32:
|
||||
value = int(selector.(uint32))
|
||||
case uint64:
|
||||
value = int(selector.(uint64))
|
||||
default:
|
||||
panic("objx: array access argument is not an integer type (this should never happen)")
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
case []{1}:
|
||||
a := object.([]{1})
|
||||
if isSet {
|
||||
a[index] = value.({1})
|
||||
} else {
|
||||
if index >= len(a) {
|
||||
if panics {
|
||||
panic(fmt.Sprintf("objx: Index %d is out of range because the []{1} only contains %d items.", index, len(a)))
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return a[index]
|
||||
}
|
||||
}
|
86
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/codegen/index.html
generated
vendored
86
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/codegen/index.html
generated
vendored
@ -1,86 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Codegen
|
||||
</title>
|
||||
<style>
|
||||
body {
|
||||
width: 800px;
|
||||
margin: auto;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
font-family: Courier;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2>
|
||||
Template
|
||||
</h2>
|
||||
<p>
|
||||
Use <code>{x}</code> as a placeholder for each argument.
|
||||
</p>
|
||||
<textarea id="template"></textarea>
|
||||
|
||||
<h2>
|
||||
Arguments (comma separated)
|
||||
</h2>
|
||||
<p>
|
||||
One block per line
|
||||
</p>
|
||||
<textarea id="args"></textarea>
|
||||
|
||||
<h2>
|
||||
Output
|
||||
</h2>
|
||||
<input id="go" type="button" value="Generate code" />
|
||||
|
||||
<textarea id="output"></textarea>
|
||||
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
|
||||
<script>
|
||||
|
||||
$(function(){
|
||||
|
||||
$("#go").click(function(){
|
||||
|
||||
var output = ""
|
||||
var template = $("#template").val()
|
||||
var args = $("#args").val()
|
||||
|
||||
// collect the args
|
||||
var argLines = args.split("\n")
|
||||
for (var line in argLines) {
|
||||
|
||||
var argLine = argLines[line];
|
||||
var thisTemp = template
|
||||
|
||||
// get individual args
|
||||
var args = argLine.split(",")
|
||||
|
||||
for (var argI in args) {
|
||||
var argText = args[argI];
|
||||
var argPlaceholder = "{" + argI + "}";
|
||||
|
||||
while (thisTemp.indexOf(argPlaceholder) > -1) {
|
||||
thisTemp = thisTemp.replace(argPlaceholder, argText);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
output += thisTemp
|
||||
|
||||
}
|
||||
|
||||
$("#output").val(output);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
286
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/codegen/template.txt
generated
vendored
286
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/codegen/template.txt
generated
vendored
@ -1,286 +0,0 @@
|
||||
/*
|
||||
{4} ({1} and []{1})
|
||||
--------------------------------------------------
|
||||
*/
|
||||
|
||||
// {4} gets the value as a {1}, returns the optionalDefault
|
||||
// value or a system default object if the value is the wrong type.
|
||||
func (v *Value) {4}(optionalDefault ...{1}) {1} {
|
||||
if s, ok := v.data.({1}); ok {
|
||||
return s
|
||||
}
|
||||
if len(optionalDefault) == 1 {
|
||||
return optionalDefault[0]
|
||||
}
|
||||
return {3}
|
||||
}
|
||||
|
||||
// Must{4} gets the value as a {1}.
|
||||
//
|
||||
// Panics if the object is not a {1}.
|
||||
func (v *Value) Must{4}() {1} {
|
||||
return v.data.({1})
|
||||
}
|
||||
|
||||
// {4}Slice gets the value as a []{1}, returns the optionalDefault
|
||||
// value or nil if the value is not a []{1}.
|
||||
func (v *Value) {4}Slice(optionalDefault ...[]{1}) []{1} {
|
||||
if s, ok := v.data.([]{1}); ok {
|
||||
return s
|
||||
}
|
||||
if len(optionalDefault) == 1 {
|
||||
return optionalDefault[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Must{4}Slice gets the value as a []{1}.
|
||||
//
|
||||
// Panics if the object is not a []{1}.
|
||||
func (v *Value) Must{4}Slice() []{1} {
|
||||
return v.data.([]{1})
|
||||
}
|
||||
|
||||
// Is{4} gets whether the object contained is a {1} or not.
|
||||
func (v *Value) Is{4}() bool {
|
||||
_, ok := v.data.({1})
|
||||
return ok
|
||||
}
|
||||
|
||||
// Is{4}Slice gets whether the object contained is a []{1} or not.
|
||||
func (v *Value) Is{4}Slice() bool {
|
||||
_, ok := v.data.([]{1})
|
||||
return ok
|
||||
}
|
||||
|
||||
// Each{4} calls the specified callback for each object
|
||||
// in the []{1}.
|
||||
//
|
||||
// Panics if the object is the wrong type.
|
||||
func (v *Value) Each{4}(callback func(int, {1}) bool) *Value {
|
||||
|
||||
for index, val := range v.Must{4}Slice() {
|
||||
carryon := callback(index, val)
|
||||
if carryon == false {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return v
|
||||
|
||||
}
|
||||
|
||||
// Where{4} uses the specified decider function to select items
|
||||
// from the []{1}. The object contained in the result will contain
|
||||
// only the selected items.
|
||||
func (v *Value) Where{4}(decider func(int, {1}) bool) *Value {
|
||||
|
||||
var selected []{1}
|
||||
|
||||
v.Each{4}(func(index int, val {1}) bool {
|
||||
shouldSelect := decider(index, val)
|
||||
if shouldSelect == false {
|
||||
selected = append(selected, val)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return &Value{data:selected}
|
||||
|
||||
}
|
||||
|
||||
// Group{4} uses the specified grouper function to group the items
|
||||
// keyed by the return of the grouper. The object contained in the
|
||||
// result will contain a map[string][]{1}.
|
||||
func (v *Value) Group{4}(grouper func(int, {1}) string) *Value {
|
||||
|
||||
groups := make(map[string][]{1})
|
||||
|
||||
v.Each{4}(func(index int, val {1}) bool {
|
||||
group := grouper(index, val)
|
||||
if _, ok := groups[group]; !ok {
|
||||
groups[group] = make([]{1}, 0)
|
||||
}
|
||||
groups[group] = append(groups[group], val)
|
||||
return true
|
||||
})
|
||||
|
||||
return &Value{data:groups}
|
||||
|
||||
}
|
||||
|
||||
// Replace{4} uses the specified function to replace each {1}s
|
||||
// by iterating each item. The data in the returned result will be a
|
||||
// []{1} containing the replaced items.
|
||||
func (v *Value) Replace{4}(replacer func(int, {1}) {1}) *Value {
|
||||
|
||||
arr := v.Must{4}Slice()
|
||||
replaced := make([]{1}, len(arr))
|
||||
|
||||
v.Each{4}(func(index int, val {1}) bool {
|
||||
replaced[index] = replacer(index, val)
|
||||
return true
|
||||
})
|
||||
|
||||
return &Value{data:replaced}
|
||||
|
||||
}
|
||||
|
||||
// Collect{4} uses the specified collector function to collect a value
|
||||
// for each of the {1}s in the slice. The data returned will be a
|
||||
// []interface{}.
|
||||
func (v *Value) Collect{4}(collector func(int, {1}) interface{}) *Value {
|
||||
|
||||
arr := v.Must{4}Slice()
|
||||
collected := make([]interface{}, len(arr))
|
||||
|
||||
v.Each{4}(func(index int, val {1}) bool {
|
||||
collected[index] = collector(index, val)
|
||||
return true
|
||||
})
|
||||
|
||||
return &Value{data:collected}
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// TESTS
|
||||
// ************************************************************
|
||||
|
||||
func Test{4}(t *testing.T) {
|
||||
|
||||
val := {1}( {2} )
|
||||
m := map[string]interface{}{"value": val, "nothing": nil}
|
||||
assert.Equal(t, val, New(m).Get("value").{4}())
|
||||
assert.Equal(t, val, New(m).Get("value").Must{4}())
|
||||
assert.Equal(t, {1}({3}), New(m).Get("nothing").{4}())
|
||||
assert.Equal(t, val, New(m).Get("nothing").{4}({2}))
|
||||
|
||||
assert.Panics(t, func() {
|
||||
New(m).Get("age").Must{4}()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test{4}Slice(t *testing.T) {
|
||||
|
||||
val := {1}( {2} )
|
||||
m := map[string]interface{}{"value": []{1}{ val }, "nothing": nil}
|
||||
assert.Equal(t, val, New(m).Get("value").{4}Slice()[0])
|
||||
assert.Equal(t, val, New(m).Get("value").Must{4}Slice()[0])
|
||||
assert.Equal(t, []{1}(nil), New(m).Get("nothing").{4}Slice())
|
||||
assert.Equal(t, val, New(m).Get("nothing").{4}Slice( []{1}{ {1}({2}) } )[0])
|
||||
|
||||
assert.Panics(t, func() {
|
||||
New(m).Get("nothing").Must{4}Slice()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestIs{4}(t *testing.T) {
|
||||
|
||||
var v *Value
|
||||
|
||||
v = &Value{data: {1}({2})}
|
||||
assert.True(t, v.Is{4}())
|
||||
|
||||
v = &Value{data: []{1}{ {1}({2}) }}
|
||||
assert.True(t, v.Is{4}Slice())
|
||||
|
||||
}
|
||||
|
||||
func TestEach{4}(t *testing.T) {
|
||||
|
||||
v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
|
||||
count := 0
|
||||
replacedVals := make([]{1}, 0)
|
||||
assert.Equal(t, v, v.Each{4}(func(i int, val {1}) bool {
|
||||
|
||||
count++
|
||||
replacedVals = append(replacedVals, val)
|
||||
|
||||
// abort early
|
||||
if i == 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
}))
|
||||
|
||||
assert.Equal(t, count, 3)
|
||||
assert.Equal(t, replacedVals[0], v.Must{4}Slice()[0])
|
||||
assert.Equal(t, replacedVals[1], v.Must{4}Slice()[1])
|
||||
assert.Equal(t, replacedVals[2], v.Must{4}Slice()[2])
|
||||
|
||||
}
|
||||
|
||||
func TestWhere{4}(t *testing.T) {
|
||||
|
||||
v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
|
||||
|
||||
selected := v.Where{4}(func(i int, val {1}) bool {
|
||||
return i%2==0
|
||||
}).Must{4}Slice()
|
||||
|
||||
assert.Equal(t, 3, len(selected))
|
||||
|
||||
}
|
||||
|
||||
func TestGroup{4}(t *testing.T) {
|
||||
|
||||
v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
|
||||
|
||||
grouped := v.Group{4}(func(i int, val {1}) string {
|
||||
return fmt.Sprintf("%v", i%2==0)
|
||||
}).data.(map[string][]{1})
|
||||
|
||||
assert.Equal(t, 2, len(grouped))
|
||||
assert.Equal(t, 3, len(grouped["true"]))
|
||||
assert.Equal(t, 3, len(grouped["false"]))
|
||||
|
||||
}
|
||||
|
||||
func TestReplace{4}(t *testing.T) {
|
||||
|
||||
v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
|
||||
|
||||
rawArr := v.Must{4}Slice()
|
||||
|
||||
replaced := v.Replace{4}(func(index int, val {1}) {1} {
|
||||
if index < len(rawArr)-1 {
|
||||
return rawArr[index+1]
|
||||
}
|
||||
return rawArr[0]
|
||||
})
|
||||
|
||||
replacedArr := replaced.Must{4}Slice()
|
||||
if assert.Equal(t, 6, len(replacedArr)) {
|
||||
assert.Equal(t, replacedArr[0], rawArr[1])
|
||||
assert.Equal(t, replacedArr[1], rawArr[2])
|
||||
assert.Equal(t, replacedArr[2], rawArr[3])
|
||||
assert.Equal(t, replacedArr[3], rawArr[4])
|
||||
assert.Equal(t, replacedArr[4], rawArr[5])
|
||||
assert.Equal(t, replacedArr[5], rawArr[0])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCollect{4}(t *testing.T) {
|
||||
|
||||
v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
|
||||
|
||||
collected := v.Collect{4}(func(index int, val {1}) interface{} {
|
||||
return index
|
||||
})
|
||||
|
||||
collectedArr := collected.MustInterSlice()
|
||||
if assert.Equal(t, 6, len(collectedArr)) {
|
||||
assert.Equal(t, collectedArr[0], 0)
|
||||
assert.Equal(t, collectedArr[1], 1)
|
||||
assert.Equal(t, collectedArr[2], 2)
|
||||
assert.Equal(t, collectedArr[3], 3)
|
||||
assert.Equal(t, collectedArr[4], 4)
|
||||
assert.Equal(t, collectedArr[5], 5)
|
||||
}
|
||||
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
Interface,interface{},"something",nil,Inter
|
||||
Map,map[string]interface{},map[string]interface{}{"name":"Tyler"},nil,MSI
|
||||
ObjxMap,(Map),New(1),New(nil),ObjxMap
|
||||
Bool,bool,true,false,Bool
|
||||
String,string,"hello","",Str
|
||||
Int,int,1,0,Int
|
||||
Int8,int8,1,0,Int8
|
||||
Int16,int16,1,0,Int16
|
||||
Int32,int32,1,0,Int32
|
||||
Int64,int64,1,0,Int64
|
||||
Uint,uint,1,0,Uint
|
||||
Uint8,uint8,1,0,Uint8
|
||||
Uint16,uint16,1,0,Uint16
|
||||
Uint32,uint32,1,0,Uint32
|
||||
Uint64,uint64,1,0,Uint64
|
||||
Uintptr,uintptr,1,0,Uintptr
|
||||
Float32,float32,1,0,Float32
|
||||
Float64,float64,1,0,Float64
|
||||
Complex64,complex64,1,0,Complex64
|
||||
Complex128,complex128,1,0,Complex128
|
13
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/constants.go
generated
vendored
13
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/constants.go
generated
vendored
@ -1,13 +0,0 @@
|
||||
package objx
|
||||
|
||||
const (
|
||||
// PathSeparator is the character used to separate the elements
|
||||
// of the keypath.
|
||||
//
|
||||
// For example, `location.address.city`
|
||||
PathSeparator string = "."
|
||||
|
||||
// SignatureSeparator is the character that is used to
|
||||
// separate the Base64 string from the security signature.
|
||||
SignatureSeparator = "_"
|
||||
)
|
117
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/conversions.go
generated
vendored
117
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/conversions.go
generated
vendored
@ -1,117 +0,0 @@
|
||||
package objx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// JSON converts the contained object to a JSON string
|
||||
// representation
|
||||
func (m Map) JSON() (string, error) {
|
||||
|
||||
result, err := json.Marshal(m)
|
||||
|
||||
if err != nil {
|
||||
err = errors.New("objx: JSON encode failed with: " + err.Error())
|
||||
}
|
||||
|
||||
return string(result), err
|
||||
|
||||
}
|
||||
|
||||
// MustJSON converts the contained object to a JSON string
|
||||
// representation and panics if there is an error
|
||||
func (m Map) MustJSON() string {
|
||||
result, err := m.JSON()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Base64 converts the contained object to a Base64 string
|
||||
// representation of the JSON string representation
|
||||
func (m Map) Base64() (string, error) {
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
jsonData, err := m.JSON()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
encoder := base64.NewEncoder(base64.StdEncoding, &buf)
|
||||
encoder.Write([]byte(jsonData))
|
||||
encoder.Close()
|
||||
|
||||
return buf.String(), nil
|
||||
|
||||
}
|
||||
|
||||
// MustBase64 converts the contained object to a Base64 string
|
||||
// representation of the JSON string representation and panics
|
||||
// if there is an error
|
||||
func (m Map) MustBase64() string {
|
||||
result, err := m.Base64()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// SignedBase64 converts the contained object to a Base64 string
|
||||
// representation of the JSON string representation and signs it
|
||||
// using the provided key.
|
||||
func (m Map) SignedBase64(key string) (string, error) {
|
||||
|
||||
base64, err := m.Base64()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
sig := HashWithKey(base64, key)
|
||||
|
||||
return base64 + SignatureSeparator + sig, nil
|
||||
|
||||
}
|
||||
|
||||
// MustSignedBase64 converts the contained object to a Base64 string
|
||||
// representation of the JSON string representation and signs it
|
||||
// using the provided key and panics if there is an error
|
||||
func (m Map) MustSignedBase64(key string) string {
|
||||
result, err := m.SignedBase64(key)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
URL Query
|
||||
------------------------------------------------
|
||||
*/
|
||||
|
||||
// URLValues creates a url.Values object from an Obj. This
|
||||
// function requires that the wrapped object be a map[string]interface{}
|
||||
func (m Map) URLValues() url.Values {
|
||||
|
||||
vals := make(url.Values)
|
||||
|
||||
for k, v := range m {
|
||||
//TODO: can this be done without sprintf?
|
||||
vals.Set(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
|
||||
return vals
|
||||
}
|
||||
|
||||
// URLQuery gets an encoded URL query representing the given
|
||||
// Obj. This function requires that the wrapped object be a
|
||||
// map[string]interface{}
|
||||
func (m Map) URLQuery() (string, error) {
|
||||
return m.URLValues().Encode(), nil
|
||||
}
|
72
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/doc.go
generated
vendored
72
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/doc.go
generated
vendored
@ -1,72 +0,0 @@
|
||||
// objx - Go package for dealing with maps, slices, JSON and other data.
|
||||
//
|
||||
// Overview
|
||||
//
|
||||
// Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes
|
||||
// a powerful `Get` method (among others) that allows you to easily and quickly get
|
||||
// access to data within the map, without having to worry too much about type assertions,
|
||||
// missing data, default values etc.
|
||||
//
|
||||
// Pattern
|
||||
//
|
||||
// Objx uses a preditable pattern to make access data from within `map[string]interface{}'s
|
||||
// easy.
|
||||
//
|
||||
// Call one of the `objx.` functions to create your `objx.Map` to get going:
|
||||
//
|
||||
// m, err := objx.FromJSON(json)
|
||||
//
|
||||
// NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong,
|
||||
// the rest will be optimistic and try to figure things out without panicking.
|
||||
//
|
||||
// Use `Get` to access the value you're interested in. You can use dot and array
|
||||
// notation too:
|
||||
//
|
||||
// m.Get("places[0].latlng")
|
||||
//
|
||||
// Once you have saught the `Value` you're interested in, you can use the `Is*` methods
|
||||
// to determine its type.
|
||||
//
|
||||
// if m.Get("code").IsStr() { /* ... */ }
|
||||
//
|
||||
// Or you can just assume the type, and use one of the strong type methods to
|
||||
// extract the real value:
|
||||
//
|
||||
// m.Get("code").Int()
|
||||
//
|
||||
// If there's no value there (or if it's the wrong type) then a default value
|
||||
// will be returned, or you can be explicit about the default value.
|
||||
//
|
||||
// Get("code").Int(-1)
|
||||
//
|
||||
// If you're dealing with a slice of data as a value, Objx provides many useful
|
||||
// methods for iterating, manipulating and selecting that data. You can find out more
|
||||
// by exploring the index below.
|
||||
//
|
||||
// Reading data
|
||||
//
|
||||
// A simple example of how to use Objx:
|
||||
//
|
||||
// // use MustFromJSON to make an objx.Map from some JSON
|
||||
// m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`)
|
||||
//
|
||||
// // get the details
|
||||
// name := m.Get("name").Str()
|
||||
// age := m.Get("age").Int()
|
||||
//
|
||||
// // get their nickname (or use their name if they
|
||||
// // don't have one)
|
||||
// nickname := m.Get("nickname").Str(name)
|
||||
//
|
||||
// Ranging
|
||||
//
|
||||
// Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For
|
||||
// example, to `range` the data, do what you would expect:
|
||||
//
|
||||
// m := objx.MustFromJSON(json)
|
||||
// for key, value := range m {
|
||||
//
|
||||
// /* ... do your magic ... */
|
||||
//
|
||||
// }
|
||||
package objx
|
222
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/map.go
generated
vendored
222
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/map.go
generated
vendored
@ -1,222 +0,0 @@
|
||||
package objx
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MSIConvertable is an interface that defines methods for converting your
|
||||
// custom types to a map[string]interface{} representation.
|
||||
type MSIConvertable interface {
|
||||
// MSI gets a map[string]interface{} (msi) representing the
|
||||
// object.
|
||||
MSI() map[string]interface{}
|
||||
}
|
||||
|
||||
// Map provides extended functionality for working with
|
||||
// untyped data, in particular map[string]interface (msi).
|
||||
type Map map[string]interface{}
|
||||
|
||||
// Value returns the internal value instance
|
||||
func (m Map) Value() *Value {
|
||||
return &Value{data: m}
|
||||
}
|
||||
|
||||
// Nil represents a nil Map.
|
||||
var Nil Map = New(nil)
|
||||
|
||||
// New creates a new Map containing the map[string]interface{} in the data argument.
|
||||
// If the data argument is not a map[string]interface, New attempts to call the
|
||||
// MSI() method on the MSIConvertable interface to create one.
|
||||
func New(data interface{}) Map {
|
||||
if _, ok := data.(map[string]interface{}); !ok {
|
||||
if converter, ok := data.(MSIConvertable); ok {
|
||||
data = converter.MSI()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return Map(data.(map[string]interface{}))
|
||||
}
|
||||
|
||||
// MSI creates a map[string]interface{} and puts it inside a new Map.
|
||||
//
|
||||
// The arguments follow a key, value pattern.
|
||||
//
|
||||
// Panics
|
||||
//
|
||||
// Panics if any key arugment is non-string or if there are an odd number of arguments.
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// To easily create Maps:
|
||||
//
|
||||
// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true))
|
||||
//
|
||||
// // creates an Map equivalent to
|
||||
// m := objx.New(map[string]interface{}{"name": "Mat", "age": 29, "subobj": map[string]interface{}{"active": true}})
|
||||
func MSI(keyAndValuePairs ...interface{}) Map {
|
||||
|
||||
newMap := make(map[string]interface{})
|
||||
keyAndValuePairsLen := len(keyAndValuePairs)
|
||||
|
||||
if keyAndValuePairsLen%2 != 0 {
|
||||
panic("objx: MSI must have an even number of arguments following the 'key, value' pattern.")
|
||||
}
|
||||
|
||||
for i := 0; i < keyAndValuePairsLen; i = i + 2 {
|
||||
|
||||
key := keyAndValuePairs[i]
|
||||
value := keyAndValuePairs[i+1]
|
||||
|
||||
// make sure the key is a string
|
||||
keyString, keyStringOK := key.(string)
|
||||
if !keyStringOK {
|
||||
panic("objx: MSI must follow 'string, interface{}' pattern. " + keyString + " is not a valid key.")
|
||||
}
|
||||
|
||||
newMap[keyString] = value
|
||||
|
||||
}
|
||||
|
||||
return New(newMap)
|
||||
}
|
||||
|
||||
// ****** Conversion Constructors
|
||||
|
||||
// MustFromJSON creates a new Map containing the data specified in the
|
||||
// jsonString.
|
||||
//
|
||||
// Panics if the JSON is invalid.
|
||||
func MustFromJSON(jsonString string) Map {
|
||||
o, err := FromJSON(jsonString)
|
||||
|
||||
if err != nil {
|
||||
panic("objx: MustFromJSON failed with error: " + err.Error())
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// FromJSON creates a new Map containing the data specified in the
|
||||
// jsonString.
|
||||
//
|
||||
// Returns an error if the JSON is invalid.
|
||||
func FromJSON(jsonString string) (Map, error) {
|
||||
|
||||
var data interface{}
|
||||
err := json.Unmarshal([]byte(jsonString), &data)
|
||||
|
||||
if err != nil {
|
||||
return Nil, err
|
||||
}
|
||||
|
||||
return New(data), nil
|
||||
|
||||
}
|
||||
|
||||
// FromBase64 creates a new Obj containing the data specified
|
||||
// in the Base64 string.
|
||||
//
|
||||
// The string is an encoded JSON string returned by Base64
|
||||
func FromBase64(base64String string) (Map, error) {
|
||||
|
||||
decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String))
|
||||
|
||||
decoded, err := ioutil.ReadAll(decoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return FromJSON(string(decoded))
|
||||
}
|
||||
|
||||
// MustFromBase64 creates a new Obj containing the data specified
|
||||
// in the Base64 string and panics if there is an error.
|
||||
//
|
||||
// The string is an encoded JSON string returned by Base64
|
||||
func MustFromBase64(base64String string) Map {
|
||||
|
||||
result, err := FromBase64(base64String)
|
||||
|
||||
if err != nil {
|
||||
panic("objx: MustFromBase64 failed with error: " + err.Error())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// FromSignedBase64 creates a new Obj containing the data specified
|
||||
// in the Base64 string.
|
||||
//
|
||||
// The string is an encoded JSON string returned by SignedBase64
|
||||
func FromSignedBase64(base64String, key string) (Map, error) {
|
||||
parts := strings.Split(base64String, SignatureSeparator)
|
||||
if len(parts) != 2 {
|
||||
return nil, errors.New("objx: Signed base64 string is malformed.")
|
||||
}
|
||||
|
||||
sig := HashWithKey(parts[0], key)
|
||||
if parts[1] != sig {
|
||||
return nil, errors.New("objx: Signature for base64 data does not match.")
|
||||
}
|
||||
|
||||
return FromBase64(parts[0])
|
||||
}
|
||||
|
||||
// MustFromSignedBase64 creates a new Obj containing the data specified
|
||||
// in the Base64 string and panics if there is an error.
|
||||
//
|
||||
// The string is an encoded JSON string returned by Base64
|
||||
func MustFromSignedBase64(base64String, key string) Map {
|
||||
|
||||
result, err := FromSignedBase64(base64String, key)
|
||||
|
||||
if err != nil {
|
||||
panic("objx: MustFromSignedBase64 failed with error: " + err.Error())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// FromURLQuery generates a new Obj by parsing the specified
|
||||
// query.
|
||||
//
|
||||
// For queries with multiple values, the first value is selected.
|
||||
func FromURLQuery(query string) (Map, error) {
|
||||
|
||||
vals, err := url.ParseQuery(query)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := make(map[string]interface{})
|
||||
for k, vals := range vals {
|
||||
m[k] = vals[0]
|
||||
}
|
||||
|
||||
return New(m), nil
|
||||
}
|
||||
|
||||
// MustFromURLQuery generates a new Obj by parsing the specified
|
||||
// query.
|
||||
//
|
||||
// For queries with multiple values, the first value is selected.
|
||||
//
|
||||
// Panics if it encounters an error
|
||||
func MustFromURLQuery(query string) Map {
|
||||
|
||||
o, err := FromURLQuery(query)
|
||||
|
||||
if err != nil {
|
||||
panic("objx: MustFromURLQuery failed with error: " + err.Error())
|
||||
}
|
||||
|
||||
return o
|
||||
|
||||
}
|
81
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/mutations.go
generated
vendored
81
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/mutations.go
generated
vendored
@ -1,81 +0,0 @@
|
||||
package objx
|
||||
|
||||
// Exclude returns a new Map with the keys in the specified []string
|
||||
// excluded.
|
||||
func (d Map) Exclude(exclude []string) Map {
|
||||
|
||||
excluded := make(Map)
|
||||
for k, v := range d {
|
||||
var shouldInclude bool = true
|
||||
for _, toExclude := range exclude {
|
||||
if k == toExclude {
|
||||
shouldInclude = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if shouldInclude {
|
||||
excluded[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return excluded
|
||||
}
|
||||
|
||||
// Copy creates a shallow copy of the Obj.
|
||||
func (m Map) Copy() Map {
|
||||
copied := make(map[string]interface{})
|
||||
for k, v := range m {
|
||||
copied[k] = v
|
||||
}
|
||||
return New(copied)
|
||||
}
|
||||
|
||||
// Merge blends the specified map with a copy of this map and returns the result.
|
||||
//
|
||||
// Keys that appear in both will be selected from the specified map.
|
||||
// This method requires that the wrapped object be a map[string]interface{}
|
||||
func (m Map) Merge(merge Map) Map {
|
||||
return m.Copy().MergeHere(merge)
|
||||
}
|
||||
|
||||
// Merge blends the specified map with this map and returns the current map.
|
||||
//
|
||||
// Keys that appear in both will be selected from the specified map. The original map
|
||||
// will be modified. This method requires that
|
||||
// the wrapped object be a map[string]interface{}
|
||||
func (m Map) MergeHere(merge Map) Map {
|
||||
|
||||
for k, v := range merge {
|
||||
m[k] = v
|
||||
}
|
||||
|
||||
return m
|
||||
|
||||
}
|
||||
|
||||
// Transform builds a new Obj giving the transformer a chance
|
||||
// to change the keys and values as it goes. This method requires that
|
||||
// the wrapped object be a map[string]interface{}
|
||||
func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map {
|
||||
newMap := make(map[string]interface{})
|
||||
for k, v := range m {
|
||||
modifiedKey, modifiedVal := transformer(k, v)
|
||||
newMap[modifiedKey] = modifiedVal
|
||||
}
|
||||
return New(newMap)
|
||||
}
|
||||
|
||||
// TransformKeys builds a new map using the specified key mapping.
|
||||
//
|
||||
// Unspecified keys will be unaltered.
|
||||
// This method requires that the wrapped object be a map[string]interface{}
|
||||
func (m Map) TransformKeys(mapping map[string]string) Map {
|
||||
return m.Transform(func(key string, value interface{}) (string, interface{}) {
|
||||
|
||||
if newKey, ok := mapping[key]; ok {
|
||||
return newKey, value
|
||||
}
|
||||
|
||||
return key, value
|
||||
})
|
||||
}
|
14
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/security.go
generated
vendored
14
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/security.go
generated
vendored
@ -1,14 +0,0 @@
|
||||
package objx
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// HashWithKey hashes the specified string using the security
|
||||
// key.
|
||||
func HashWithKey(data, key string) string {
|
||||
hash := sha1.New()
|
||||
hash.Write([]byte(data + ":" + key))
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
17
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/tests.go
generated
vendored
17
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/tests.go
generated
vendored
@ -1,17 +0,0 @@
|
||||
package objx
|
||||
|
||||
// Has gets whether there is something at the specified selector
|
||||
// or not.
|
||||
//
|
||||
// If m is nil, Has will always return false.
|
||||
func (m Map) Has(selector string) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
return !m.Get(selector).IsNil()
|
||||
}
|
||||
|
||||
// IsNil gets whether the data is nil or not.
|
||||
func (v *Value) IsNil() bool {
|
||||
return v == nil || v.data == nil
|
||||
}
|
2881
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/type_specific_codegen.go
generated
vendored
2881
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/type_specific_codegen.go
generated
vendored
File diff suppressed because it is too large
Load Diff
13
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/value.go
generated
vendored
13
vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/value.go
generated
vendored
@ -1,13 +0,0 @@
|
||||
package objx
|
||||
|
||||
// Value provides methods for extracting interface{} data in various
|
||||
// types.
|
||||
type Value struct {
|
||||
// data contains the raw data being managed by this Value
|
||||
data interface{}
|
||||
}
|
||||
|
||||
// Data returns the raw data contained by this Value
|
||||
func (v *Value) Data() interface{} {
|
||||
return v.data
|
||||
}
|
Loading…
Reference in New Issue
Block a user