mirror of
https://github.com/linuxkit/linuxkit.git
synced 2026-01-24 08:22:19 +00:00
infrakit: Move the hyperkit instance plugin into the source directory
- The tools directory ideally should not contain source code - Removes double vendoring of packagages - Makes it easer to hook the build into the top-level Makefile Eventually, the plugin should be moved to the infrakit repo. Signed-off-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
This commit is contained in:
201
vendor/github.com/docker/infrakit/LICENSE
generated
vendored
Normal file
201
vendor/github.com/docker/infrakit/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 2016 Docker, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
235
vendor/github.com/docker/infrakit/README.md
generated
vendored
Normal file
235
vendor/github.com/docker/infrakit/README.md
generated
vendored
Normal file
@@ -0,0 +1,235 @@
|
||||
InfraKit
|
||||
========
|
||||
|
||||
[](https://circleci.com/gh/docker/infrakit)
|
||||
[](https://goreportcard.com/report/github.com/docker/infrakit)
|
||||
[](https://codecov.io/github/docker/infrakit?branch=master)
|
||||
|
||||
_InfraKit_ is a toolkit for creating and managing declarative, self-healing infrastructure.
|
||||
It breaks infrastructure automation down into simple, pluggable components. These components work together to actively
|
||||
ensure the infrastructure state matches the user's specifications.
|
||||
Although _InfraKit_ emphasizes primitives for building self-healing infrastructure, it also can be used passively like
|
||||
conventional tools.
|
||||
|
||||
To get started, try the [tutorial](docs/tutorial.md).
|
||||
|
||||
### Who InfraKit is for
|
||||
|
||||
_InfraKit_ is designed to support setup and management of base infrastructure. For example, it can help you manage a
|
||||
system like a cluster or container orchestrator, ideally relieving you of building custom release and maintenance tools.
|
||||
As a result, it is a low-level tool intended to be used by infrastructure operators directly or indirectly
|
||||
(as a toolkit) through a higher-level tool. Since _InfraKit_ is pluggable, it allows you to manage resources in diverse
|
||||
environments while using shared components and consistent interfaces.
|
||||
|
||||
## Plugins
|
||||
_InfraKit_ makes extensive use of _Plugins_ to manage arbitrary systems in diverse environments, which can be composed
|
||||
to meet different needs.
|
||||
|
||||
See the [plugins](docs/plugins) documentation for more details.
|
||||
|
||||
|
||||
## Building
|
||||
### Your Environment
|
||||
|
||||
Make sure you check out the project following a convention for building Go projects. For example,
|
||||
|
||||
```shell
|
||||
|
||||
# Install Go - https://golang.org/dl/
|
||||
# Assuming your go compiler is in /usr/local/go
|
||||
export PATH=/usr/local/go/bin:$PATH
|
||||
|
||||
# Your dev environment
|
||||
mkdir -p ~/go
|
||||
export GOPATH=!$
|
||||
export PATH=$GOPATH/bin:$PATH
|
||||
|
||||
mkdir -p ~/go/src/github.com/docker
|
||||
cd !$
|
||||
git clone git@github.com:docker/infrakit.git
|
||||
cd infrakit
|
||||
```
|
||||
|
||||
We recommended go version 1.7.1 or greater for all platforms.
|
||||
|
||||
Also install a few build tools:
|
||||
```shell
|
||||
make get-tools
|
||||
```
|
||||
|
||||
### Running tests
|
||||
```shell
|
||||
$ make ci
|
||||
```
|
||||
|
||||
### Binaries
|
||||
```shell
|
||||
$ make binaries
|
||||
```
|
||||
Executables will be placed in the `./build` directory.
|
||||
This will produce binaries for tools and several reference Plugin implementations:
|
||||
+ [`infrakit`](cmd/cli/README.md): a command line interface to interact with plugins
|
||||
+ [`infrakit-group-default`](cmd/group/README.md): the default [Group plugin](./pkg/spi/group)
|
||||
+ [`infrakit-instance-file`](examples/instance/file): an Instance plugin using dummy files to represent instances
|
||||
+ [`infrakit-instance-terraform`](examples/instance/terraform):
|
||||
an Instance plugin integrating [Terraform](https://www.terraform.io)
|
||||
+ [`infrakit-instance-vagrant`](examples/instance/vagrant):
|
||||
an Instance plugin using [Vagrant](https://www.vagrantup.com/)
|
||||
+ [`infrakit-instance-maas`](examples/instance/maas):
|
||||
an Instance plugin using [MaaS](https://maas.io)
|
||||
+ [`infrakit-flavor-vanilla`](examples/flavor/vanilla):
|
||||
a Flavor plugin for plain vanilla set up with user data and labels
|
||||
+ [`infrakit-flavor-zookeeper`](examples/flavor/zookeeper):
|
||||
a Flavor plugin for [Apache ZooKeeper](https://zookeeper.apache.org/) ensemble members
|
||||
+ [`infrakit-flavor-swarm`](examples/flavor/swarm):
|
||||
a Flavor plugin for Docker in [Swarm mode](https://docs.docker.com/engine/swarm/).
|
||||
|
||||
All provided binaries have a `help` sub-command to get usage and a `version` sub-command to identify the build revision.
|
||||
|
||||
|
||||
# Design
|
||||
|
||||
## Configuration
|
||||
_InfraKit_ uses JSON for configuration because it is composable and a widely accepted format for many
|
||||
infrastructure SDKs and tools. Since the system is highly component-driven, our JSON format follows
|
||||
simple patterns to support the composition of components.
|
||||
|
||||
A common pattern for a JSON object looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"SomeKey": "ValueForTheKey",
|
||||
"Properties": {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
There is only one `Properties` field in this JSON and its value is a JSON object. The opaque
|
||||
JSON value for `Properties` is decoded via the Go `Spec` struct defined within the package of the plugin --
|
||||
for example -- [`vanilla.Spec`](pkg/plugin/flavor/vanilla/flavor.go).
|
||||
|
||||
The JSON above is a _value_, but the type of the value belongs outside the structure. For example, the
|
||||
default Group [Spec](pkg/plugin/group/types/types.go) is composed of an Instance plugin, a Flavor plugin, and an
|
||||
Allocation:
|
||||
|
||||
```json
|
||||
{
|
||||
"ID": "name-of-the-group",
|
||||
"Properties": {
|
||||
"Allocation": {
|
||||
},
|
||||
"Instance": {
|
||||
"Plugin": "name-of-the-instance-plugin",
|
||||
"Properties": {
|
||||
}
|
||||
},
|
||||
"Flavor": {
|
||||
"Plugin": "name-of-the-flavor-plugin",
|
||||
"Properties": {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
The group's Spec has `Instance` and `Flavor` fields which are used to indicate the type, and the value of the
|
||||
fields follow the pattern of `<some_key>` and `Properties` as shown above.
|
||||
|
||||
The `Allocation` determines how the Group is managed. Allocation has two properties:
|
||||
- `Size`: an integer for the number of instances to maintain in the Group
|
||||
- `LogicalIDs`: a list of string identifiers, one will be associated with each Instance
|
||||
|
||||
Exactly one of these fields must be set, which defines whether the Group is treated as 'cattle' (`Size`) or 'pets'
|
||||
(`LogicalIDs`). It is up to the Instance and Flavor plugins to determine how to use `LogicalID` values.
|
||||
|
||||
As an example, if you wanted to manage a Group of NGINX servers, you could
|
||||
write a custom Group plugin for ultimate customization. The most concise configuration looks something like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"ID": "nginx",
|
||||
"Plugin": "my-nginx-group-plugin",
|
||||
"Properties": {
|
||||
"port": 8080
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
However, you would likely prefer to use the default Group plugin and implement a Flavor plugin to focus on
|
||||
application-specific behavior. This gives you immediate support for any infrastructure that has an Instance plugin.
|
||||
Your resulting configuration might look something like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"ID": "nginx",
|
||||
"Plugin": "group",
|
||||
"Properties": {
|
||||
"Allocation": {
|
||||
"Size": 10
|
||||
},
|
||||
"Instance": {
|
||||
"Plugin": "aws",
|
||||
"Properties": {
|
||||
"region": "us-west-2",
|
||||
"ami": "ami-123456"
|
||||
}
|
||||
},
|
||||
"Flavor": {
|
||||
"Plugin": "nginx",
|
||||
"Properties": {
|
||||
"port": 8080
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Once the configuration is ready, you will tell a Group plugin to
|
||||
+ watch it
|
||||
+ update it
|
||||
+ destroy it
|
||||
|
||||
Watching the group as specified in the configuration means that the Group plugin will create
|
||||
the instances if they don't already exist. New instances will be created if for any reason
|
||||
existing instances have disappeared such that the state doesn't match your specifications.
|
||||
|
||||
Updating the group tells the Group plugin that your configuration may have changed. It will
|
||||
then determine the changes necessary to ensure the state of the infrastructure matches the new
|
||||
specification.
|
||||
|
||||
## Docs
|
||||
|
||||
Additional documentation can be found [here](docs).
|
||||
|
||||
## Reporting security issues
|
||||
|
||||
The maintainers take security seriously. If you discover a security issue,
|
||||
please bring it to their attention right away!
|
||||
|
||||
Please **DO NOT** file a public issue, instead send your report privately to
|
||||
[security@docker.com](mailto:security@docker.com).
|
||||
|
||||
Security reports are greatly appreciated and we will publicly thank you for it.
|
||||
We also like to send gifts—if you're into Docker schwag, make sure to let
|
||||
us know. We currently do not offer a paid security bounty program, but are not
|
||||
ruling it out in the future.
|
||||
|
||||
|
||||
## Design goals
|
||||
|
||||
_InfraKit_ is currently focused on supporting setup and management of base infrastructure, such as a cluster
|
||||
orchestrator. The image below illustrates an architecture we are working towards supporting - a Docker cluster in Swarm
|
||||
mode.
|
||||
|
||||

|
||||
|
||||
This configuration co-locates _InfraKit_ with Swarm manager nodes and offers high availability of _InfraKit_ itself and
|
||||
Swarm managers (using attached storage). _InfraKit_ is shown managing two groups - managers and workers that will be
|
||||
continuously monitored, and may be modified with rolling updates.
|
||||
|
||||
Countless configurations are possible with _InfraKit_, but we believe achieving support for this configuration will
|
||||
enable a large number of real-world use cases.
|
||||
|
||||
## Copyright and license
|
||||
|
||||
Copyright © 2016 Docker, Inc. All rights reserved. Released under the Apache 2.0
|
||||
license. See [LICENSE](LICENSE) for the full license text.
|
||||
74
vendor/github.com/docker/infrakit/pkg/broker/server/interceptor.go
generated
vendored
Normal file
74
vendor/github.com/docker/infrakit/pkg/broker/server/interceptor.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Interceptor implements http Handler and is used to intercept incoming requests to subscribe
|
||||
// to topics and perform some validations before allowing the subscription to be established.
|
||||
// Tasks that the interceptor can do include authn and authz, topic validation, etc.
|
||||
type Interceptor struct {
|
||||
|
||||
// Do is the body of the http handler -- required
|
||||
Do http.HandlerFunc
|
||||
|
||||
// Pre is called to before actual subscription to a topic happens.
|
||||
// This is the hook where validation and authentication / authorization checks happen.
|
||||
Pre func(topic string, headers map[string][]string) error
|
||||
|
||||
// Post is called when the client disconnects. This is optional.
|
||||
Post func(topic string)
|
||||
}
|
||||
|
||||
// ServeHTTP calls the before and after subscribe methods.
|
||||
func (i *Interceptor) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
topic := clean(req.URL.Query().Get("topic"))
|
||||
|
||||
// need to strip out the / because it was added by the client
|
||||
if strings.Index(topic, "/") == 0 {
|
||||
topic = topic[1:]
|
||||
}
|
||||
|
||||
err := i.Pre(topic, req.Header)
|
||||
if err != nil {
|
||||
log.Warningln("Error:", err)
|
||||
http.Error(rw, err.Error(), getStatusCode(err))
|
||||
return
|
||||
}
|
||||
|
||||
i.Do.ServeHTTP(rw, req)
|
||||
|
||||
if i.Post != nil {
|
||||
i.Post(topic)
|
||||
}
|
||||
}
|
||||
|
||||
func getStatusCode(e error) int {
|
||||
switch e.(type) {
|
||||
case ErrInvalidTopic:
|
||||
return http.StatusNotFound
|
||||
case ErrNotAuthorized:
|
||||
return http.StatusUnauthorized
|
||||
default:
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
|
||||
// ErrInvalidTopic is the error raised when topic is invalid
|
||||
type ErrInvalidTopic string
|
||||
|
||||
func (e ErrInvalidTopic) Error() string {
|
||||
return fmt.Sprintf("invalid topic: %s", string(e))
|
||||
}
|
||||
|
||||
// ErrNotAuthorized is the error raised when the user isn't authorized
|
||||
type ErrNotAuthorized string
|
||||
|
||||
func (e ErrNotAuthorized) Error() string {
|
||||
return fmt.Sprintf("not authorized: %s", string(e))
|
||||
}
|
||||
27
vendor/github.com/docker/infrakit/pkg/broker/server/server.go
generated
vendored
Normal file
27
vendor/github.com/docker/infrakit/pkg/broker/server/server.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ListenAndServeOnSocket starts a minimal server (mostly for testing) listening at the given unix socket path.
|
||||
func ListenAndServeOnSocket(socketPath string, optionalURLPattern ...string) (*Broker, error) {
|
||||
urlPattern := "/"
|
||||
if len(optionalURLPattern) > 0 {
|
||||
urlPattern = optionalURLPattern[0]
|
||||
}
|
||||
listener, err := net.Listen("unix", socketPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
broker := NewBroker()
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle(urlPattern, broker)
|
||||
httpServer := &http.Server{
|
||||
Handler: mux,
|
||||
}
|
||||
go httpServer.Serve(listener)
|
||||
|
||||
return broker, nil
|
||||
}
|
||||
266
vendor/github.com/docker/infrakit/pkg/broker/server/sse.go
generated
vendored
Normal file
266
vendor/github.com/docker/infrakit/pkg/broker/server/sse.go
generated
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/armon/go-radix"
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
// the amount of time to wait when pushing a message to
|
||||
// a slow client or a client that closed after `range clients` started.
|
||||
const patience time.Duration = time.Second * 1
|
||||
|
||||
type subscription struct {
|
||||
topic string
|
||||
exactMatch bool
|
||||
ch chan []byte
|
||||
}
|
||||
|
||||
type event struct {
|
||||
topic string
|
||||
data []byte
|
||||
}
|
||||
|
||||
// Broker is the event message broker
|
||||
type Broker struct {
|
||||
|
||||
// Close this to stop
|
||||
stop chan struct{}
|
||||
|
||||
// Close this to stop the run loop
|
||||
finish chan struct{}
|
||||
|
||||
// Events are pushed to this channel by the main events-gathering routine
|
||||
notifier chan *event
|
||||
|
||||
// New client connections
|
||||
newClients chan subscription
|
||||
|
||||
// Closed client connections
|
||||
closingClients chan subscription
|
||||
|
||||
// Client connections registry
|
||||
clients *radix.Tree
|
||||
|
||||
// how many clients
|
||||
count int
|
||||
}
|
||||
|
||||
// NewBroker returns an instance of the broker
|
||||
func NewBroker() *Broker {
|
||||
b := &Broker{
|
||||
stop: make(chan struct{}),
|
||||
finish: make(chan struct{}),
|
||||
notifier: make(chan *event, 1),
|
||||
newClients: make(chan subscription),
|
||||
closingClients: make(chan subscription),
|
||||
clients: radix.New(),
|
||||
}
|
||||
go b.run()
|
||||
return b
|
||||
}
|
||||
|
||||
// Stop stops the broker and exits the goroutine
|
||||
func (b *Broker) Stop() {
|
||||
close(b.stop)
|
||||
}
|
||||
|
||||
func clean(topic string) string {
|
||||
if len(topic) == 0 || topic == "." {
|
||||
return "/"
|
||||
}
|
||||
|
||||
if topic[0] != '/' {
|
||||
return "/" + topic
|
||||
}
|
||||
return topic
|
||||
}
|
||||
|
||||
//Check the topic ends with `/`
|
||||
func checkExactMatch(topic string) bool {
|
||||
return strings.LastIndex(topic, "/") != len(topic)-1
|
||||
}
|
||||
|
||||
// Publish publishes a message at the topic
|
||||
func (b *Broker) Publish(topic string, data interface{}, optionalTimeout ...time.Duration) error {
|
||||
any, err := types.AnyValue(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
topic = clean(topic)
|
||||
|
||||
if len(optionalTimeout) > 0 {
|
||||
select {
|
||||
case b.notifier <- &event{topic: topic, data: any.Bytes()}:
|
||||
case <-time.After(optionalTimeout[0]):
|
||||
return fmt.Errorf("timeout sending %v", topic)
|
||||
}
|
||||
} else {
|
||||
b.notifier <- &event{topic: topic, data: any.Bytes()}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServerHTTP implements the HTTP handler
|
||||
func (b *Broker) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
defer func() {
|
||||
if v := recover(); v != nil {
|
||||
log.Warningln("broker.ServeHTTP recovered:", v)
|
||||
}
|
||||
}()
|
||||
|
||||
topic := clean(req.URL.Query().Get("topic"))
|
||||
|
||||
// flusher is required for streaming
|
||||
flusher, ok := rw.(http.Flusher)
|
||||
if !ok {
|
||||
http.Error(rw, "Streaming unsupported!", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "text/event-stream")
|
||||
rw.Header().Set("Cache-Control", "no-cache")
|
||||
rw.Header().Set("Connection", "keep-alive")
|
||||
rw.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
// Each connection registers its own message channel with the Broker's connections registry
|
||||
messageChan := make(chan []byte)
|
||||
|
||||
// Signal the broker that we have a new connection
|
||||
b.newClients <- subscription{topic: topic, exactMatch: checkExactMatch(topic), ch: messageChan}
|
||||
|
||||
// Remove this client from the map of connected clients
|
||||
// when this handler exits.
|
||||
defer func() {
|
||||
b.closingClients <- subscription{topic: topic, ch: messageChan}
|
||||
}()
|
||||
|
||||
// Listen to connection close and un-register messageChan
|
||||
notify := rw.(http.CloseNotifier).CloseNotify()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-b.stop:
|
||||
close(b.finish)
|
||||
return
|
||||
|
||||
case <-notify:
|
||||
return
|
||||
default:
|
||||
|
||||
// Write to the ResponseWriter
|
||||
// Server Sent Events compatible
|
||||
fmt.Fprintf(rw, "data: %s\n\n", <-messageChan)
|
||||
|
||||
// Flush the data immediatly instead of buffering it for later.
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Broker) run() {
|
||||
for {
|
||||
select {
|
||||
case <-b.finish:
|
||||
|
||||
// Disconnect all clients
|
||||
b.clients.Walk(
|
||||
func(key string, value interface{}) bool {
|
||||
chset, ok := value.(map[chan []byte]bool)
|
||||
if !ok {
|
||||
panic("assert-failed")
|
||||
}
|
||||
for ch := range chset {
|
||||
log.Infoln("Closing client connection at", key, "ch=", ch)
|
||||
close(ch)
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
log.Infoln("Broker finished")
|
||||
return
|
||||
|
||||
case subscription := <-b.newClients:
|
||||
|
||||
// A new client has connected.
|
||||
// Register their message channel
|
||||
subs := map[chan []byte]bool{subscription.ch: subscription.exactMatch}
|
||||
v, has := b.clients.Get(subscription.topic)
|
||||
if has {
|
||||
if v, ok := v.(map[chan []byte]bool); !ok {
|
||||
panic("assert-failed: not a map of channels")
|
||||
} else {
|
||||
v[subscription.ch] = subscription.exactMatch
|
||||
subs = v
|
||||
}
|
||||
}
|
||||
|
||||
b.clients.Insert(subscription.topic, subs)
|
||||
b.count++
|
||||
log.Infof("Connected: topic=%s => %d registered clients, ch=%v", subscription.topic, b.count, subscription.ch)
|
||||
|
||||
case subscription := <-b.closingClients:
|
||||
|
||||
// A client has dettached and we want to stop sending messages
|
||||
if v, has := b.clients.Get(subscription.topic); has {
|
||||
if subs, ok := v.(map[chan []byte]bool); !ok {
|
||||
panic("assert-failed: not a map of channels")
|
||||
|
||||
} else {
|
||||
|
||||
delete(subs, subscription.ch)
|
||||
|
||||
if len(subs) == 0 {
|
||||
b.clients.Delete(subscription.topic)
|
||||
} else {
|
||||
b.clients.Insert(subscription.topic, subs)
|
||||
}
|
||||
|
||||
b.count--
|
||||
log.Infof("Disconnected: topic=%s => %d registered clients, ch=%v", subscription.topic, b.count, subscription.ch)
|
||||
}
|
||||
}
|
||||
|
||||
case event, open := <-b.notifier:
|
||||
|
||||
if !open {
|
||||
log.Infoln("Stopping broker")
|
||||
return
|
||||
}
|
||||
|
||||
// Remove any \n because it's meaningful in SSE spec.
|
||||
// We could use base64 encode, but it hurts interoperability with browser/ javascript clients.
|
||||
data := bytes.Replace(event.data, []byte("\n"), nil, -1)
|
||||
|
||||
b.clients.WalkPath(event.topic,
|
||||
|
||||
func(key string, value interface{}) bool {
|
||||
chset, ok := value.(map[chan []byte]bool)
|
||||
if !ok {
|
||||
panic("assert-failed")
|
||||
}
|
||||
|
||||
for ch, exact := range chset {
|
||||
if exact && event.topic != key {
|
||||
return false
|
||||
}
|
||||
select {
|
||||
case ch <- data:
|
||||
case <-time.After(patience):
|
||||
log.Print("Skipping client.")
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
16
vendor/github.com/docker/infrakit/pkg/cli/logging.go
generated
vendored
Normal file
16
vendor/github.com/docker/infrakit/pkg/cli/logging.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package cli
|
||||
|
||||
import log "github.com/Sirupsen/logrus"
|
||||
|
||||
// DefaultLogLevel is the default log level value.
|
||||
var DefaultLogLevel = len(log.AllLevels) - 2
|
||||
|
||||
// SetLogLevel adjusts the logrus level.
|
||||
func SetLogLevel(level int) {
|
||||
if level > len(log.AllLevels)-1 {
|
||||
level = len(log.AllLevels) - 1
|
||||
} else if level < 0 {
|
||||
level = 0
|
||||
}
|
||||
log.SetLevel(log.AllLevels[level])
|
||||
}
|
||||
47
vendor/github.com/docker/infrakit/pkg/cli/serverutil.go
generated
vendored
Normal file
47
vendor/github.com/docker/infrakit/pkg/cli/serverutil.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/infrakit/pkg/discovery"
|
||||
"github.com/docker/infrakit/pkg/rpc/server"
|
||||
)
|
||||
|
||||
// EnsureDirExists makes sure the directory where the socket file will be placed exists.
|
||||
func EnsureDirExists(dir string) {
|
||||
os.MkdirAll(dir, 0700)
|
||||
}
|
||||
|
||||
// RunPlugin runs a plugin server, advertising with the provided name for discovery.
|
||||
// The plugin should conform to the rpc call convention as implemented in the rpc package.
|
||||
func RunPlugin(name string, plugin server.VersionedInterface, more ...server.VersionedInterface) {
|
||||
|
||||
dir := discovery.Dir()
|
||||
EnsureDirExists(dir)
|
||||
|
||||
socketPath := path.Join(dir, name)
|
||||
pidPath := path.Join(dir, name+".pid")
|
||||
|
||||
stoppable, err := server.StartPluginAtPath(socketPath, plugin, more...)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
// write PID file
|
||||
err = ioutil.WriteFile(pidPath, []byte(fmt.Sprintf("%v", os.Getpid())), 0644)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
log.Infoln("PID file at", pidPath)
|
||||
if stoppable != nil {
|
||||
stoppable.AwaitStopped()
|
||||
}
|
||||
|
||||
// clean up
|
||||
os.Remove(pidPath)
|
||||
log.Infoln("Removed PID file at", pidPath)
|
||||
}
|
||||
45
vendor/github.com/docker/infrakit/pkg/cli/version.go
generated
vendored
Normal file
45
vendor/github.com/docker/infrakit/pkg/cli/version.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
// Version is the build release identifier.
|
||||
Version = "Unspecified"
|
||||
|
||||
// Revision is the build source control revision.
|
||||
Revision = "Unspecified"
|
||||
)
|
||||
|
||||
var info = map[string]map[string]interface{}{}
|
||||
|
||||
// RegisterInfo allows any packages that use this register additional information to be displayed by the command.
|
||||
// For example, a swarm flavor could register the docker api version. This allows us to selectively incorporate
|
||||
// only required dependencies based on package registration (in their init()) without explicitly pulling unused
|
||||
// dependencies.
|
||||
func RegisterInfo(key string, data map[string]interface{}) {
|
||||
info[key] = data
|
||||
}
|
||||
|
||||
// VersionCommand creates a cobra Command that prints build version information.
|
||||
func VersionCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "print build version information",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("\n%-24s: %v", "Version", Version)
|
||||
fmt.Printf("\n%-24s: %v", "Revision", Revision)
|
||||
for k, m := range info {
|
||||
fmt.Printf("\n\n%s", k)
|
||||
for kk, vv := range m {
|
||||
fmt.Printf("\n%-24s: %v", kk, vv)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
fmt.Println()
|
||||
},
|
||||
}
|
||||
}
|
||||
106
vendor/github.com/docker/infrakit/pkg/discovery/dir.go
generated
vendored
Normal file
106
vendor/github.com/docker/infrakit/pkg/discovery/dir.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package discovery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/infrakit/pkg/plugin"
|
||||
)
|
||||
|
||||
type errNotUnixSocket string
|
||||
|
||||
func (e errNotUnixSocket) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// IsErrNotUnixSocket returns true if the error is due to the file not being a valid unix socket.
|
||||
func IsErrNotUnixSocket(e error) bool {
|
||||
_, is := e.(errNotUnixSocket)
|
||||
return is
|
||||
}
|
||||
|
||||
type dirPluginDiscovery struct {
|
||||
dir string
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// Find returns a plugin by name
|
||||
func (r *dirPluginDiscovery) Find(name plugin.Name) (*plugin.Endpoint, error) {
|
||||
lookup, _ := name.GetLookupAndType()
|
||||
plugins, err := r.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, exists := plugins[lookup]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("Plugin not found: %s (looked up using %s)", name, lookup)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// newDirPluginDiscovery creates a registry instance with the given file directory path.
|
||||
func newDirPluginDiscovery(dir string) (*dirPluginDiscovery, error) {
|
||||
d := &dirPluginDiscovery{dir: dir}
|
||||
|
||||
// Perform a dummy read to catch obvious issues early (such as the directory not existing).
|
||||
_, err := d.List()
|
||||
return d, err
|
||||
}
|
||||
|
||||
func (r *dirPluginDiscovery) dirLookup(entry os.FileInfo) (*plugin.Endpoint, error) {
|
||||
if entry.Mode()&os.ModeSocket != 0 {
|
||||
socketPath := filepath.Join(r.dir, entry.Name())
|
||||
return &plugin.Endpoint{
|
||||
Protocol: "unix",
|
||||
Address: socketPath,
|
||||
Name: entry.Name(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, errNotUnixSocket(fmt.Sprintf("File is not a socket: %s", entry))
|
||||
}
|
||||
|
||||
// List returns a list of plugins known, keyed by the name
|
||||
func (r *dirPluginDiscovery) List() (map[string]*plugin.Endpoint, error) {
|
||||
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
log.Debugln("Opening:", r.dir)
|
||||
entries, err := ioutil.ReadDir(r.dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
plugins := map[string]*plugin.Endpoint{}
|
||||
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
|
||||
instance, err := r.dirLookup(entry)
|
||||
|
||||
if err != nil {
|
||||
if !IsErrNotUnixSocket(err) {
|
||||
log.Warningln("Loading plugin err=", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if instance == nil {
|
||||
log.Warningln("Plugin in nil=")
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugln("Discovered plugin at", instance.Address)
|
||||
plugins[instance.Name] = instance
|
||||
}
|
||||
}
|
||||
|
||||
return plugins, nil
|
||||
}
|
||||
60
vendor/github.com/docker/infrakit/pkg/discovery/discovery.go
generated
vendored
Normal file
60
vendor/github.com/docker/infrakit/pkg/discovery/discovery.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package discovery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
|
||||
"github.com/docker/infrakit/pkg/plugin"
|
||||
)
|
||||
|
||||
// Plugins provides access to plugin discovery.
|
||||
type Plugins interface {
|
||||
// Find looks up the plugin by name. The name can be of the form $lookup[/$subtype]. See GetLookupAndType().
|
||||
Find(name plugin.Name) (*plugin.Endpoint, error)
|
||||
List() (map[string]*plugin.Endpoint, error)
|
||||
}
|
||||
|
||||
const (
|
||||
// PluginDirEnvVar is the environment variable that may be used to customize the plugin discovery path.
|
||||
PluginDirEnvVar = "INFRAKIT_PLUGINS_DIR"
|
||||
)
|
||||
|
||||
// Dir returns the directory to use for plugin discovery, which may be customized by the environment.
|
||||
func Dir() string {
|
||||
if pluginDir := os.Getenv(PluginDirEnvVar); pluginDir != "" {
|
||||
return pluginDir
|
||||
}
|
||||
|
||||
home := os.Getenv("HOME")
|
||||
if usr, err := user.Current(); err == nil {
|
||||
home = usr.HomeDir
|
||||
}
|
||||
return path.Join(home, ".infrakit/plugins")
|
||||
}
|
||||
|
||||
// NewPluginDiscovery creates a plugin discovery based on the environment configuration.
|
||||
func NewPluginDiscovery() (Plugins, error) {
|
||||
return NewPluginDiscoveryWithDirectory(Dir())
|
||||
}
|
||||
|
||||
// NewPluginDiscoveryWithDirectory creates a plugin discovery based on the directory given.
|
||||
func NewPluginDiscoveryWithDirectory(pluginDir string) (Plugins, error) {
|
||||
stat, err := os.Stat(pluginDir)
|
||||
if err == nil {
|
||||
if !stat.IsDir() {
|
||||
return nil, fmt.Errorf("Plugin dir %s is a file", pluginDir)
|
||||
}
|
||||
} else {
|
||||
if os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(pluginDir, 0700); err != nil {
|
||||
return nil, fmt.Errorf("Failed to create plugin dir %s: %s", pluginDir, err)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("Failed to access plugin dir %s: %s", pluginDir, err)
|
||||
}
|
||||
}
|
||||
|
||||
return newDirPluginDiscovery(pluginDir)
|
||||
}
|
||||
30
vendor/github.com/docker/infrakit/pkg/plugin/metadata/path.go
generated
vendored
Normal file
30
vendor/github.com/docker/infrakit/pkg/plugin/metadata/path.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/infrakit/pkg/spi/metadata"
|
||||
)
|
||||
|
||||
// Path returns the path compoments of a / separated path
|
||||
func Path(path string) metadata.Path {
|
||||
return metadata.Path(strings.Split(filepath.Clean(path), "/"))
|
||||
}
|
||||
|
||||
// PathFromStrings returns the path from a list of strings
|
||||
func PathFromStrings(a string, b ...string) metadata.Path {
|
||||
if a != "" {
|
||||
return metadata.Path(append([]string{a}, b...))
|
||||
}
|
||||
return metadata.Path(b)
|
||||
}
|
||||
|
||||
// String returns the string representation of path
|
||||
func String(p metadata.Path) string {
|
||||
s := strings.Join([]string(p), "/")
|
||||
if len(s) == 0 {
|
||||
return "."
|
||||
}
|
||||
return s
|
||||
}
|
||||
90
vendor/github.com/docker/infrakit/pkg/plugin/metadata/plugin.go
generated
vendored
Normal file
90
vendor/github.com/docker/infrakit/pkg/plugin/metadata/plugin.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/infrakit/pkg/spi/metadata"
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
// NewPluginFromData creates a plugin out of a simple data map. Note the updates to the map
|
||||
// is not guarded and synchronized with the reads.
|
||||
func NewPluginFromData(data map[string]interface{}) metadata.Plugin {
|
||||
return &plugin{data: data}
|
||||
}
|
||||
|
||||
// NewPluginFromChannel returns a plugin implementation where reads and writes are serialized
|
||||
// via channel of functions that have a view to the metadata. Closing the write channel stops
|
||||
// the serialized read/writes and falls back to unserialized reads.
|
||||
func NewPluginFromChannel(writes <-chan func(map[string]interface{})) metadata.Plugin {
|
||||
|
||||
readChan := make(chan func(map[string]interface{}))
|
||||
p := &plugin{reads: readChan}
|
||||
|
||||
go func() {
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Warningln("Plugin stopped:", r)
|
||||
}
|
||||
}()
|
||||
|
||||
data := map[string]interface{}{}
|
||||
for {
|
||||
select {
|
||||
case writer, open := <-writes:
|
||||
if !open {
|
||||
close(readChan)
|
||||
p.reads = nil
|
||||
return
|
||||
}
|
||||
writer(data)
|
||||
|
||||
case reader := <-p.reads:
|
||||
copy := data
|
||||
reader(copy)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return p
|
||||
}
|
||||
|
||||
type plugin struct {
|
||||
data map[string]interface{}
|
||||
reads chan func(data map[string]interface{})
|
||||
}
|
||||
|
||||
// List returns a list of *child nodes* given a path, which is specified as a slice
|
||||
// where for i > j path[i] is the parent of path[j]
|
||||
func (p *plugin) List(path metadata.Path) ([]string, error) {
|
||||
if p.reads == nil && p.data != nil {
|
||||
return List(path, p.data), nil
|
||||
}
|
||||
|
||||
children := make(chan []string)
|
||||
|
||||
p.reads <- func(data map[string]interface{}) {
|
||||
children <- List(path, data)
|
||||
return
|
||||
}
|
||||
|
||||
return <-children, nil
|
||||
}
|
||||
|
||||
// Get retrieves the value at path given.
|
||||
func (p *plugin) Get(path metadata.Path) (*types.Any, error) {
|
||||
if p.reads == nil && p.data != nil {
|
||||
return types.AnyValue(Get(path, p.data))
|
||||
}
|
||||
|
||||
value := make(chan *types.Any)
|
||||
err := make(chan error)
|
||||
|
||||
p.reads <- func(data map[string]interface{}) {
|
||||
v, e := types.AnyValue(Get(path, data))
|
||||
value <- v
|
||||
err <- e
|
||||
return
|
||||
}
|
||||
|
||||
return <-value, <-err
|
||||
}
|
||||
230
vendor/github.com/docker/infrakit/pkg/plugin/metadata/reflect.go
generated
vendored
Normal file
230
vendor/github.com/docker/infrakit/pkg/plugin/metadata/reflect.go
generated
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
var (
|
||||
indexRoot = "\\[(([+|-]*[0-9]+)|((.*)=(.*)))\\]$"
|
||||
arrayIndexExp = regexp.MustCompile("(.*)" + indexRoot)
|
||||
indexExp = regexp.MustCompile("^" + indexRoot)
|
||||
)
|
||||
|
||||
// Put sets the attribute of an object at path to the given value
|
||||
func Put(path []string, value interface{}, object map[string]interface{}) bool {
|
||||
return put(path, value, object)
|
||||
}
|
||||
|
||||
// Get returns the attribute of the object at path
|
||||
func Get(path []string, object interface{}) interface{} {
|
||||
return get(path, object)
|
||||
}
|
||||
|
||||
// GetValue returns the attribute of the object at path, as serialized blob
|
||||
func GetValue(path []string, object interface{}) (*types.Any, error) {
|
||||
if any, is := object.(*types.Any); is {
|
||||
return any, nil
|
||||
}
|
||||
return types.AnyValue(Get(path, object))
|
||||
}
|
||||
|
||||
// List lists the members at the path
|
||||
func List(path []string, object interface{}) []string {
|
||||
list := []string{}
|
||||
v := get(path, object)
|
||||
if v == nil {
|
||||
return list
|
||||
}
|
||||
|
||||
val := reflect.Indirect(reflect.ValueOf(v))
|
||||
|
||||
if any, is := v.(*types.Any); is {
|
||||
var temp interface{}
|
||||
if err := any.Decode(&temp); err == nil {
|
||||
val = reflect.ValueOf(temp)
|
||||
}
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Slice:
|
||||
// this is a slice, so return the name as '[%d]'
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
list = append(list, fmt.Sprintf("[%d]", i))
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
for _, k := range val.MapKeys() {
|
||||
list = append(list, k.String())
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
vt := val.Type()
|
||||
for i := 0; i < vt.NumField(); i++ {
|
||||
if vt.Field(i).PkgPath == "" {
|
||||
list = append(list, vt.Field(i).Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(list)
|
||||
return list
|
||||
}
|
||||
|
||||
func put(p []string, value interface{}, store map[string]interface{}) bool {
|
||||
if len(p) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
key := p[0]
|
||||
if key == "" {
|
||||
return put(p[1:], value, store)
|
||||
}
|
||||
// check if key is an array index of the form <1>[<2>]
|
||||
matches := arrayIndexExp.FindStringSubmatch(key)
|
||||
if len(matches) > 2 && matches[1] != "" {
|
||||
key = matches[1]
|
||||
p = append([]string{key, fmt.Sprintf("[%s]", matches[2])}, p[1:]...)
|
||||
return put(p, value, store)
|
||||
}
|
||||
|
||||
s := reflect.Indirect(reflect.ValueOf(store))
|
||||
switch s.Kind() {
|
||||
case reflect.Slice:
|
||||
return false // not supported
|
||||
|
||||
case reflect.Map:
|
||||
if reflect.TypeOf(p[0]).AssignableTo(s.Type().Key()) {
|
||||
m := s.MapIndex(reflect.ValueOf(p[0]))
|
||||
if !m.IsValid() {
|
||||
m = reflect.ValueOf(map[string]interface{}{})
|
||||
s.SetMapIndex(reflect.ValueOf(p[0]), m)
|
||||
}
|
||||
if len(p) > 1 {
|
||||
return put(p[1:], value, m.Interface().(map[string]interface{}))
|
||||
}
|
||||
s.SetMapIndex(reflect.ValueOf(p[0]), reflect.ValueOf(value))
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func get(path []string, object interface{}) (value interface{}) {
|
||||
if f, is := object.(func() interface{}); is {
|
||||
object = f()
|
||||
}
|
||||
|
||||
if len(path) == 0 {
|
||||
return object
|
||||
}
|
||||
|
||||
if any, is := object.(*types.Any); is {
|
||||
var temp interface{}
|
||||
if err := any.Decode(&temp); err == nil {
|
||||
return get(path, temp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
key := path[0]
|
||||
|
||||
switch key {
|
||||
case ".":
|
||||
return object
|
||||
case "":
|
||||
return get(path[1:], object)
|
||||
}
|
||||
|
||||
// check if key is an array index of the form <1>[<2>]
|
||||
matches := arrayIndexExp.FindStringSubmatch(key)
|
||||
if len(matches) > 2 && matches[1] != "" {
|
||||
key = matches[1]
|
||||
path = append([]string{key, fmt.Sprintf("[%s]", matches[2])}, path[1:]...)
|
||||
return get(path, object)
|
||||
}
|
||||
|
||||
v := reflect.Indirect(reflect.ValueOf(object))
|
||||
switch v.Kind() {
|
||||
case reflect.Slice:
|
||||
i := 0
|
||||
matches = indexExp.FindStringSubmatch(key)
|
||||
if len(matches) > 0 {
|
||||
if matches[2] != "" {
|
||||
// numeric index
|
||||
if index, err := strconv.Atoi(matches[1]); err == nil {
|
||||
switch {
|
||||
case index >= 0 && v.Len() > index:
|
||||
i = index
|
||||
case index < 0 && v.Len() > -index: // negative index like python
|
||||
i = v.Len() + index
|
||||
}
|
||||
}
|
||||
return get(path[1:], v.Index(i).Interface())
|
||||
|
||||
} else if matches[3] != "" {
|
||||
// equality search index for 'field=check'
|
||||
lhs := matches[4] // supports another select expression for extracting deeply from the struct
|
||||
rhs := matches[5]
|
||||
// loop through the array looking for field that matches the check value
|
||||
for j := 0; j < v.Len(); j++ {
|
||||
if el := get(tokenize(lhs), v.Index(j).Interface()); el != nil {
|
||||
if fmt.Sprintf("%v", el) == rhs {
|
||||
return get(path[1:], v.Index(j).Interface())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
value := v.MapIndex(reflect.ValueOf(key))
|
||||
if value.IsValid() {
|
||||
return get(path[1:], value.Interface())
|
||||
}
|
||||
case reflect.Struct:
|
||||
fv := v.FieldByName(key)
|
||||
if !fv.IsValid() {
|
||||
return nil
|
||||
}
|
||||
if !fv.CanInterface() {
|
||||
return nil
|
||||
}
|
||||
return get(path[1:], fv.Interface())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// With quoting to support azure rm type names: e.g. Microsoft.Network/virtualNetworks
|
||||
// This will split a sting like /Resources/'Microsoft.Network/virtualNetworks'/managerSubnet/Name" into
|
||||
// [ , Resources, Microsoft.Network/virtualNetworks, managerSubnet, Name]
|
||||
func tokenize(s string) []string {
|
||||
if len(s) == 0 {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
a := []string{}
|
||||
start := 0
|
||||
quoted := false
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
case '/':
|
||||
if !quoted {
|
||||
a = append(a, strings.Replace(s[start:i], "'", "", -1))
|
||||
start = i + 1
|
||||
}
|
||||
case '\'':
|
||||
quoted = !quoted
|
||||
}
|
||||
}
|
||||
if start < len(s)-1 {
|
||||
a = append(a, strings.Replace(s[start:], "'", "", -1))
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
45
vendor/github.com/docker/infrakit/pkg/plugin/name.go
generated
vendored
Normal file
45
vendor/github.com/docker/infrakit/pkg/plugin/name.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Name is a reference to the plugin. Places where it appears include JSON files as type of field `Plugin`.
|
||||
type Name string
|
||||
|
||||
// GetLookupAndType returns the plugin name for lookup and sub-type supported by the plugin.
|
||||
// The name follows a microformat of $plugin[/$subtype] where $plugin is used for the discovery / lookup by name.
|
||||
// The $subtype is used for the Type parameter in the RPC requests.
|
||||
// Example: instance-file/json means lookup socket file 'instance-file' and the type is 'json'.
|
||||
func (r Name) GetLookupAndType() (string, string) {
|
||||
name := string(r)
|
||||
if first := strings.Index(name, "/"); first >= 0 {
|
||||
return name[0:first], name[first+1:]
|
||||
}
|
||||
return name, ""
|
||||
}
|
||||
|
||||
// String returns the string representation
|
||||
func (r Name) String() string {
|
||||
return string(r)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the JSON marshaler interface
|
||||
func (r Name) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf(`"%s"`, r.String())), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the JSON unmarshaler interface
|
||||
func (r *Name) UnmarshalJSON(data []byte) error {
|
||||
str := string(data)
|
||||
start := strings.Index(str, "\"")
|
||||
last := strings.LastIndex(str, "\"")
|
||||
if start == 0 && last == len(str)-1 {
|
||||
str = str[start+1 : last]
|
||||
} else {
|
||||
return fmt.Errorf("bad-format-for-name:%v", string(data))
|
||||
}
|
||||
*r = Name(str)
|
||||
return nil
|
||||
}
|
||||
97
vendor/github.com/docker/infrakit/pkg/plugin/plugin.go
generated
vendored
Normal file
97
vendor/github.com/docker/infrakit/pkg/plugin/plugin.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"github.com/docker/infrakit/pkg/spi"
|
||||
"github.com/docker/infrakit/pkg/template"
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
// Spec models a canonical pattern of fields that exist in a struct/ map / union that indicates the block is a plugin.
|
||||
type Spec struct {
|
||||
|
||||
// Plugin is the name of the plugin
|
||||
Plugin Name
|
||||
|
||||
// Properties is the configuration of the plugin
|
||||
Properties *types.Any
|
||||
}
|
||||
|
||||
// Informer is the interface that gives information about the plugin such as version and interface methods
|
||||
type Informer interface {
|
||||
|
||||
// GetInfo returns metadata about the plugin
|
||||
GetInfo() (Info, error)
|
||||
|
||||
// GetFunctions returns metadata about the plugin's template functions, if the plugin supports templating.
|
||||
GetFunctions() (map[string][]template.Function, error)
|
||||
}
|
||||
|
||||
// Info is metadata for the plugin
|
||||
type Info struct {
|
||||
|
||||
// Vendor captures vendor-specific information about this plugin
|
||||
Vendor *spi.VendorInfo
|
||||
|
||||
// Implements is a list of plugin interface and versions this plugin supports
|
||||
Implements []spi.InterfaceSpec
|
||||
|
||||
// Interfaces (optional) is a slice of interface descriptions by the type and version
|
||||
Interfaces []InterfaceDescription `json:",omitempty"`
|
||||
}
|
||||
|
||||
// InterfaceDescription is a holder for RPC interface version and method descriptions
|
||||
type InterfaceDescription struct {
|
||||
spi.InterfaceSpec
|
||||
Methods []MethodDescription
|
||||
}
|
||||
|
||||
// MethodDescription contains information about the RPC method such as the request and response
|
||||
// example structs. The request value can be used as an example input, possibly with example
|
||||
// plugin-custom properties if the underlying plugin implements the InputExample interface.
|
||||
// The response value gives an example of the example response.
|
||||
type MethodDescription struct {
|
||||
// Request is the RPC request example
|
||||
Request Request
|
||||
|
||||
// Response is the RPC response example
|
||||
Response Response
|
||||
}
|
||||
|
||||
// Request models the RPC request payload
|
||||
type Request struct {
|
||||
|
||||
// Version is the version of the JSON RPC protocol
|
||||
Version string `json:"jsonrpc"`
|
||||
|
||||
// Method is the rpc method to use in the payload field 'method'
|
||||
Method string `json:"method"`
|
||||
|
||||
// Params contains example inputs. This can be a zero value struct or one with defaults
|
||||
Params interface{} `json:"params"`
|
||||
|
||||
// ID is the request is
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// Response is the RPC response struct
|
||||
type Response struct {
|
||||
|
||||
// Result is the result of the call
|
||||
Result interface{} `json:"result"`
|
||||
|
||||
// ID is id matching the request ID
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// Endpoint is the address of the plugin service
|
||||
type Endpoint struct {
|
||||
|
||||
// Name is the key used to refer to this plugin in all JSON configs
|
||||
Name string
|
||||
|
||||
// Protocol is the transport protocol -- unix, tcp, etc.
|
||||
Protocol string
|
||||
|
||||
// Address is the how to connect - socket file, host:port, etc.
|
||||
Address string
|
||||
}
|
||||
76
vendor/github.com/docker/infrakit/pkg/rpc/client/client.go
generated
vendored
Normal file
76
vendor/github.com/docker/infrakit/pkg/rpc/client/client.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/infrakit/pkg/spi"
|
||||
"github.com/gorilla/rpc/v2/json2"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
http http.Client
|
||||
addr string
|
||||
}
|
||||
|
||||
// New creates a new Client that communicates with a unix socket and validates the remote API.
|
||||
func New(socketPath string, api spi.InterfaceSpec) (Client, error) {
|
||||
dialUnix := func(proto, addr string) (conn net.Conn, err error) {
|
||||
return net.Dial("unix", socketPath)
|
||||
}
|
||||
|
||||
unvalidatedClient := &client{addr: socketPath, http: http.Client{Transport: &http.Transport{Dial: dialUnix}}}
|
||||
cl := &handshakingClient{client: unvalidatedClient, iface: api, lock: &sync.Mutex{}}
|
||||
|
||||
// check handshake
|
||||
if err := cl.handshake(); err != nil {
|
||||
// Note - we still return the client with the possibility of doing a handshake later on
|
||||
// if we provide an api for the plugin to recheck later. This way, individual components
|
||||
// can stay running and recalibrate themselves after the user has corrected the problems.
|
||||
return cl, err
|
||||
}
|
||||
return cl, nil
|
||||
}
|
||||
|
||||
func (c client) Addr() string {
|
||||
return c.addr
|
||||
}
|
||||
|
||||
func (c client) Call(method string, arg interface{}, result interface{}) error {
|
||||
message, err := json2.EncodeClientRequest(method, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", "http://a/", bytes.NewReader(message))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
requestData, err := httputil.DumpRequest(req, true)
|
||||
if err == nil {
|
||||
log.Debugf("Sending request %s", string(requestData))
|
||||
} else {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
responseData, err := httputil.DumpResponse(resp, true)
|
||||
if err == nil {
|
||||
log.Debugf("Received response %s", string(responseData))
|
||||
} else {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
return json2.DecodeClientResponse(resp.Body, result)
|
||||
}
|
||||
88
vendor/github.com/docker/infrakit/pkg/rpc/client/handshake.go
generated
vendored
Normal file
88
vendor/github.com/docker/infrakit/pkg/rpc/client/handshake.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/infrakit/pkg/rpc"
|
||||
"github.com/docker/infrakit/pkg/spi"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type handshakingClient struct {
|
||||
client Client
|
||||
iface spi.InterfaceSpec
|
||||
|
||||
// handshakeResult handles the tri-state outcome of handshake state:
|
||||
// - handshake has not yet completed (nil)
|
||||
// - handshake completed successfully (non-nil result, nil error)
|
||||
// - handshake failed (non-nil result, non-nil error)
|
||||
handshakeResult *handshakeResult
|
||||
|
||||
// lock guards handshakeResult
|
||||
lock *sync.Mutex
|
||||
}
|
||||
|
||||
type handshakeResult struct {
|
||||
err error
|
||||
}
|
||||
|
||||
type errVersionMismatch string
|
||||
|
||||
// Error implements error interface
|
||||
func (e errVersionMismatch) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// IsErrVersionMismatch return true if the error is from mismatched api versions.
|
||||
func IsErrVersionMismatch(e error) bool {
|
||||
_, is := e.(errVersionMismatch)
|
||||
return is
|
||||
}
|
||||
|
||||
func (c *handshakingClient) handshake() error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if c.handshakeResult == nil {
|
||||
req := rpc.ImplementsRequest{}
|
||||
resp := rpc.ImplementsResponse{}
|
||||
|
||||
if err := c.client.Call("Handshake.Implements", req, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := fmt.Errorf("Plugin does not support interface %v", c.iface)
|
||||
if resp.APIs != nil {
|
||||
for _, iface := range resp.APIs {
|
||||
if iface.Name == c.iface.Name {
|
||||
if iface.Version == c.iface.Version {
|
||||
err = nil
|
||||
break
|
||||
} else {
|
||||
err = errVersionMismatch(fmt.Sprintf(
|
||||
"Plugin supports %s interface version %s, client requires %s",
|
||||
iface.Name,
|
||||
iface.Version,
|
||||
c.iface.Version))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.handshakeResult = &handshakeResult{err: err}
|
||||
}
|
||||
|
||||
return c.handshakeResult.err
|
||||
}
|
||||
|
||||
func (c *handshakingClient) Addr() string {
|
||||
return c.client.Addr()
|
||||
}
|
||||
|
||||
func (c *handshakingClient) Call(method string, arg interface{}, result interface{}) error {
|
||||
if err := c.handshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.client.Call(method, arg, result)
|
||||
}
|
||||
48
vendor/github.com/docker/infrakit/pkg/rpc/client/info.go
generated
vendored
Normal file
48
vendor/github.com/docker/infrakit/pkg/rpc/client/info.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/infrakit/pkg/plugin"
|
||||
"github.com/docker/infrakit/pkg/rpc"
|
||||
"github.com/docker/infrakit/pkg/template"
|
||||
)
|
||||
|
||||
// NewPluginInfoClient returns a plugin informer that can give metadata about a plugin
|
||||
func NewPluginInfoClient(socketPath string) *InfoClient {
|
||||
dialUnix := func(proto, addr string) (conn net.Conn, err error) {
|
||||
return net.Dial("unix", socketPath)
|
||||
}
|
||||
return &InfoClient{client: &http.Client{Transport: &http.Transport{Dial: dialUnix}}}
|
||||
}
|
||||
|
||||
// InfoClient is the client for retrieving plugin info
|
||||
type InfoClient struct {
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// GetInfo implements the Info interface and returns the metadata about the plugin
|
||||
func (i *InfoClient) GetInfo() (plugin.Info, error) {
|
||||
meta := plugin.Info{}
|
||||
resp, err := i.client.Get("http://d" + rpc.URLAPI)
|
||||
if err != nil {
|
||||
return meta, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
err = json.NewDecoder(resp.Body).Decode(&meta)
|
||||
return meta, err
|
||||
}
|
||||
|
||||
// GetFunctions returns metadata about the plugin's template functions, if the plugin supports templating.
|
||||
func (i *InfoClient) GetFunctions() (map[string][]template.Function, error) {
|
||||
meta := map[string][]template.Function{}
|
||||
resp, err := i.client.Get("http://d" + rpc.URLFunctions)
|
||||
if err != nil {
|
||||
return meta, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
err = json.NewDecoder(resp.Body).Decode(&meta)
|
||||
return meta, err
|
||||
}
|
||||
11
vendor/github.com/docker/infrakit/pkg/rpc/client/rpc.go
generated
vendored
Normal file
11
vendor/github.com/docker/infrakit/pkg/rpc/client/rpc.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package client
|
||||
|
||||
// Client allows execution of RPCs.
|
||||
type Client interface {
|
||||
|
||||
// Addr returns the address -- e.g. unix socket path
|
||||
Addr() string
|
||||
|
||||
// Call invokes an RPC method with an argument and a pointer to a result that will hold the return value.
|
||||
Call(method string, arg interface{}, result interface{}) error
|
||||
}
|
||||
25
vendor/github.com/docker/infrakit/pkg/rpc/handshake.go
generated
vendored
Normal file
25
vendor/github.com/docker/infrakit/pkg/rpc/handshake.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/infrakit/pkg/spi"
|
||||
)
|
||||
|
||||
// ImplementsRequest is the rpc wrapper for the Implements method args.
|
||||
type ImplementsRequest struct {
|
||||
}
|
||||
|
||||
// ImplementsResponse is the rpc wrapper for the Implements return value.
|
||||
type ImplementsResponse struct {
|
||||
APIs []spi.InterfaceSpec
|
||||
}
|
||||
|
||||
// Handshake is a simple RPC object for doing handshake
|
||||
type Handshake []spi.InterfaceSpec
|
||||
|
||||
// Implements responds to a request for the supported plugin interfaces.
|
||||
func (h Handshake) Implements(_ *http.Request, req *ImplementsRequest, resp *ImplementsResponse) error {
|
||||
resp.APIs = []spi.InterfaceSpec(h)
|
||||
return nil
|
||||
}
|
||||
21
vendor/github.com/docker/infrakit/pkg/rpc/info.go
generated
vendored
Normal file
21
vendor/github.com/docker/infrakit/pkg/rpc/info.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package rpc
|
||||
|
||||
const (
|
||||
// URLAPI is the well-known HTTP GET endpoint that retrieves description of the plugin's interfaces.
|
||||
URLAPI = "/info/api.json"
|
||||
|
||||
// URLFunctions exposes the templates functions that are available via this plugin
|
||||
URLFunctions = "/info/functions.json"
|
||||
|
||||
// URLEventsPrefix is the prefix of the events endpoint
|
||||
URLEventsPrefix = "/events"
|
||||
)
|
||||
|
||||
// InputExample is the interface implemented by the rpc implementations for
|
||||
// group, instance, and flavor to set example input using custom/ vendored data types.
|
||||
type InputExample interface {
|
||||
|
||||
// SetExampleProperties updates the parameter with example properties.
|
||||
// The request param must be a pointer
|
||||
SetExampleProperties(request interface{})
|
||||
}
|
||||
75
vendor/github.com/docker/infrakit/pkg/rpc/instance/client.go
generated
vendored
Normal file
75
vendor/github.com/docker/infrakit/pkg/rpc/instance/client.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package instance
|
||||
|
||||
import (
|
||||
"github.com/docker/infrakit/pkg/plugin"
|
||||
rpc_client "github.com/docker/infrakit/pkg/rpc/client"
|
||||
"github.com/docker/infrakit/pkg/spi/instance"
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
// NewClient returns a plugin interface implementation connected to a plugin
|
||||
func NewClient(name plugin.Name, socketPath string) (instance.Plugin, error) {
|
||||
rpcClient, err := rpc_client.New(socketPath, instance.InterfaceSpec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &client{name: name, client: rpcClient}, nil
|
||||
}
|
||||
|
||||
type client struct {
|
||||
name plugin.Name
|
||||
client rpc_client.Client
|
||||
}
|
||||
|
||||
// Validate performs local validation on a provision request.
|
||||
func (c client) Validate(properties *types.Any) error {
|
||||
_, instanceType := c.name.GetLookupAndType()
|
||||
req := ValidateRequest{Properties: properties, Type: instanceType}
|
||||
resp := ValidateResponse{}
|
||||
|
||||
return c.client.Call("Instance.Validate", req, &resp)
|
||||
}
|
||||
|
||||
// Provision creates a new instance based on the spec.
|
||||
func (c client) Provision(spec instance.Spec) (*instance.ID, error) {
|
||||
_, instanceType := c.name.GetLookupAndType()
|
||||
req := ProvisionRequest{Spec: spec, Type: instanceType}
|
||||
resp := ProvisionResponse{}
|
||||
|
||||
if err := c.client.Call("Instance.Provision", req, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.ID, nil
|
||||
}
|
||||
|
||||
// Label labels the instance
|
||||
func (c client) Label(instance instance.ID, labels map[string]string) error {
|
||||
_, instanceType := c.name.GetLookupAndType()
|
||||
req := LabelRequest{Type: instanceType, Instance: instance, Labels: labels}
|
||||
resp := LabelResponse{}
|
||||
|
||||
return c.client.Call("Instance.Label", req, &resp)
|
||||
}
|
||||
|
||||
// Destroy terminates an existing instance.
|
||||
func (c client) Destroy(instance instance.ID) error {
|
||||
_, instanceType := c.name.GetLookupAndType()
|
||||
req := DestroyRequest{Instance: instance, Type: instanceType}
|
||||
resp := DestroyResponse{}
|
||||
|
||||
return c.client.Call("Instance.Destroy", req, &resp)
|
||||
}
|
||||
|
||||
// DescribeInstances returns descriptions of all instances matching all of the provided tags.
|
||||
func (c client) DescribeInstances(tags map[string]string) ([]instance.Description, error) {
|
||||
_, instanceType := c.name.GetLookupAndType()
|
||||
req := DescribeInstancesRequest{Tags: tags, Type: instanceType}
|
||||
resp := DescribeInstancesResponse{}
|
||||
|
||||
err := c.client.Call("Instance.DescribeInstances", req, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Descriptions, nil
|
||||
}
|
||||
154
vendor/github.com/docker/infrakit/pkg/rpc/instance/service.go
generated
vendored
Normal file
154
vendor/github.com/docker/infrakit/pkg/rpc/instance/service.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
package instance
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/infrakit/pkg/spi"
|
||||
"github.com/docker/infrakit/pkg/spi/instance"
|
||||
)
|
||||
|
||||
// PluginServer returns a RPCService that conforms to the net/rpc rpc call convention.
|
||||
func PluginServer(p instance.Plugin) *Instance {
|
||||
return &Instance{plugin: p, typedPlugins: map[string]instance.Plugin{}}
|
||||
}
|
||||
|
||||
// PluginServerWithTypes which supports multiple types of instance plugins. The de-multiplexing
|
||||
// is done by the server's RPC method implementations.
|
||||
func PluginServerWithTypes(typed map[string]instance.Plugin) *Instance {
|
||||
return &Instance{typedPlugins: typed}
|
||||
}
|
||||
|
||||
// Instance is the JSON RPC service representing the Instance Plugin. It must be exported in order to be
|
||||
// registered by the rpc server package.
|
||||
type Instance struct {
|
||||
plugin instance.Plugin // the default plugin
|
||||
typedPlugins map[string]instance.Plugin // by type, as qualified in the name of the plugin
|
||||
}
|
||||
|
||||
// VendorInfo returns a metadata object about the plugin, if the plugin implements it.
|
||||
func (p *Instance) VendorInfo() *spi.VendorInfo {
|
||||
// TODO(chungers) - support typed plugins
|
||||
if p.plugin == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if m, is := p.plugin.(spi.Vendor); is {
|
||||
return m.VendorInfo()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetExampleProperties sets the rpc request with any example properties/ custom type
|
||||
func (p *Instance) SetExampleProperties(request interface{}) {
|
||||
// TODO(chungers) - support typed plugins
|
||||
if p.plugin == nil {
|
||||
return
|
||||
}
|
||||
|
||||
i, is := p.plugin.(spi.InputExample)
|
||||
if !is {
|
||||
return
|
||||
}
|
||||
example := i.ExampleProperties()
|
||||
if example == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch request := request.(type) {
|
||||
case *ValidateRequest:
|
||||
request.Properties = example
|
||||
case *ProvisionRequest:
|
||||
request.Spec.Properties = example
|
||||
}
|
||||
}
|
||||
|
||||
// ImplementedInterface returns the interface implemented by this RPC service.
|
||||
func (p *Instance) ImplementedInterface() spi.InterfaceSpec {
|
||||
return instance.InterfaceSpec
|
||||
}
|
||||
|
||||
func (p *Instance) getPlugin(instanceType string) instance.Plugin {
|
||||
if instanceType == "" {
|
||||
return p.plugin
|
||||
}
|
||||
if p, has := p.typedPlugins[instanceType]; has {
|
||||
return p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate performs local validation on a provision request.
|
||||
func (p *Instance) Validate(_ *http.Request, req *ValidateRequest, resp *ValidateResponse) error {
|
||||
c := p.getPlugin(req.Type)
|
||||
if c == nil {
|
||||
return fmt.Errorf("no-plugin:%s", req.Type)
|
||||
}
|
||||
resp.Type = req.Type
|
||||
err := c.Validate(req.Properties)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.OK = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Provision creates a new instance based on the spec.
|
||||
func (p *Instance) Provision(_ *http.Request, req *ProvisionRequest, resp *ProvisionResponse) error {
|
||||
resp.Type = req.Type
|
||||
c := p.getPlugin(req.Type)
|
||||
if c == nil {
|
||||
return fmt.Errorf("no-plugin:%s", req.Type)
|
||||
}
|
||||
id, err := c.Provision(req.Spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.ID = id
|
||||
return nil
|
||||
}
|
||||
|
||||
// Label labels the instance
|
||||
func (p *Instance) Label(_ *http.Request, req *LabelRequest, resp *LabelResponse) error {
|
||||
resp.Type = req.Type
|
||||
c := p.getPlugin(req.Type)
|
||||
if c == nil {
|
||||
return fmt.Errorf("no-plugin:%s", req.Type)
|
||||
}
|
||||
err := c.Label(req.Instance, req.Labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.OK = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Destroy terminates an existing instance.
|
||||
func (p *Instance) Destroy(_ *http.Request, req *DestroyRequest, resp *DestroyResponse) error {
|
||||
resp.Type = req.Type
|
||||
c := p.getPlugin(req.Type)
|
||||
if c == nil {
|
||||
return fmt.Errorf("no-plugin:%s", req.Type)
|
||||
}
|
||||
err := c.Destroy(req.Instance)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.OK = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// DescribeInstances returns descriptions of all instances matching all of the provided tags.
|
||||
func (p *Instance) DescribeInstances(_ *http.Request, req *DescribeInstancesRequest, resp *DescribeInstancesResponse) error {
|
||||
resp.Type = req.Type
|
||||
c := p.getPlugin(req.Type)
|
||||
if c == nil {
|
||||
return fmt.Errorf("no-plugin:%s", req.Type)
|
||||
}
|
||||
desc, err := c.DescribeInstances(req.Tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Descriptions = desc
|
||||
return nil
|
||||
}
|
||||
67
vendor/github.com/docker/infrakit/pkg/rpc/instance/types.go
generated
vendored
Normal file
67
vendor/github.com/docker/infrakit/pkg/rpc/instance/types.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
package instance
|
||||
|
||||
import (
|
||||
"github.com/docker/infrakit/pkg/spi/instance"
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
// ValidateRequest is the rpc wrapper for the Validate method args
|
||||
type ValidateRequest struct {
|
||||
Type string
|
||||
Properties *types.Any
|
||||
}
|
||||
|
||||
// ValidateResponse is the rpc wrapper for the Validate response values
|
||||
type ValidateResponse struct {
|
||||
Type string
|
||||
OK bool
|
||||
}
|
||||
|
||||
// ProvisionRequest is the rpc wrapper for Provision request
|
||||
type ProvisionRequest struct {
|
||||
Type string
|
||||
Spec instance.Spec
|
||||
}
|
||||
|
||||
// ProvisionResponse is the rpc wrapper for Provision response
|
||||
type ProvisionResponse struct {
|
||||
Type string
|
||||
ID *instance.ID
|
||||
}
|
||||
|
||||
// LabelRequest is the rpc wrapper for Label request
|
||||
type LabelRequest struct {
|
||||
Type string
|
||||
Instance instance.ID
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// LabelResponse is the rpc wrapper for Label response
|
||||
type LabelResponse struct {
|
||||
Type string
|
||||
OK bool
|
||||
}
|
||||
|
||||
// DestroyRequest is the rpc wrapper for Destroy request
|
||||
type DestroyRequest struct {
|
||||
Type string
|
||||
Instance instance.ID
|
||||
}
|
||||
|
||||
// DestroyResponse is the rpc wrapper for Destroy response
|
||||
type DestroyResponse struct {
|
||||
Type string
|
||||
OK bool
|
||||
}
|
||||
|
||||
// DescribeInstancesRequest is the rpc wrapper for DescribeInstances request
|
||||
type DescribeInstancesRequest struct {
|
||||
Type string
|
||||
Tags map[string]string
|
||||
}
|
||||
|
||||
// DescribeInstancesResponse is the rpc wrapper for the DescribeInstances response
|
||||
type DescribeInstancesResponse struct {
|
||||
Type string
|
||||
Descriptions []instance.Description
|
||||
}
|
||||
44
vendor/github.com/docker/infrakit/pkg/rpc/metadata/client.go
generated
vendored
Normal file
44
vendor/github.com/docker/infrakit/pkg/rpc/metadata/client.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
rpc_client "github.com/docker/infrakit/pkg/rpc/client"
|
||||
"github.com/docker/infrakit/pkg/spi/metadata"
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
// NewClient returns a plugin interface implementation connected to a remote plugin
|
||||
func NewClient(socketPath string) (metadata.Plugin, error) {
|
||||
rpcClient, err := rpc_client.New(socketPath, metadata.InterfaceSpec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &client{client: rpcClient}, nil
|
||||
}
|
||||
|
||||
// Adapt converts a rpc client to a Metadata plugin object
|
||||
func Adapt(rpcClient rpc_client.Client) metadata.Plugin {
|
||||
return &client{client: rpcClient}
|
||||
}
|
||||
|
||||
type client struct {
|
||||
client rpc_client.Client
|
||||
}
|
||||
|
||||
// List returns a list of nodes under path.
|
||||
func (c client) List(path metadata.Path) ([]string, error) {
|
||||
req := ListRequest{Path: path}
|
||||
resp := ListResponse{}
|
||||
err := c.client.Call("Metadata.List", req, &resp)
|
||||
return resp.Nodes, err
|
||||
}
|
||||
|
||||
// Get retrieves the metadata at path.
|
||||
func (c client) Get(path metadata.Path) (*types.Any, error) {
|
||||
req := GetRequest{Path: path}
|
||||
resp := GetResponse{}
|
||||
err := c.client.Call("Metadata.Get", req, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Value, err
|
||||
}
|
||||
180
vendor/github.com/docker/infrakit/pkg/rpc/metadata/service.go
generated
vendored
Normal file
180
vendor/github.com/docker/infrakit/pkg/rpc/metadata/service.go
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/docker/infrakit/pkg/spi"
|
||||
"github.com/docker/infrakit/pkg/spi/metadata"
|
||||
"github.com/docker/infrakit/pkg/template"
|
||||
)
|
||||
|
||||
// PluginServer returns a Metadata that conforms to the net/rpc rpc call convention.
|
||||
func PluginServer(p metadata.Plugin) *Metadata {
|
||||
return &Metadata{plugin: p}
|
||||
}
|
||||
|
||||
// PluginServerWithTypes which supports multiple types of metadata plugins. The de-multiplexing
|
||||
// is done by the server's RPC method implementations.
|
||||
func PluginServerWithTypes(typed map[string]metadata.Plugin) *Metadata {
|
||||
return &Metadata{typedPlugins: typed}
|
||||
}
|
||||
|
||||
// Metadata the exported type needed to conform to json-rpc call convention
|
||||
type Metadata struct {
|
||||
plugin metadata.Plugin
|
||||
typedPlugins map[string]metadata.Plugin // by type, as qualified in the name of the plugin
|
||||
}
|
||||
|
||||
// WithBase sets the base plugin to the given plugin object
|
||||
func (p *Metadata) WithBase(m metadata.Plugin) *Metadata {
|
||||
p.plugin = m
|
||||
return p
|
||||
}
|
||||
|
||||
// WithTypes sets the typed plugins to the given map of plugins (by type name)
|
||||
func (p *Metadata) WithTypes(typed map[string]metadata.Plugin) *Metadata {
|
||||
p.typedPlugins = typed
|
||||
return p
|
||||
}
|
||||
|
||||
// VendorInfo returns a metadata object about the plugin, if the plugin implements it. See spi.Vendor
|
||||
func (p *Metadata) VendorInfo() *spi.VendorInfo {
|
||||
// TODO(chungers) - support typed plugins
|
||||
if p.plugin == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if m, is := p.plugin.(spi.Vendor); is {
|
||||
return m.VendorInfo()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Funcs implements the template.FunctionExporter method to expose help for plugin's template functions
|
||||
func (p *Metadata) Funcs() []template.Function {
|
||||
f, is := p.plugin.(template.FunctionExporter)
|
||||
if !is {
|
||||
return []template.Function{}
|
||||
}
|
||||
return f.Funcs()
|
||||
}
|
||||
|
||||
// Types implements server.TypedFunctionExporter
|
||||
func (p *Metadata) Types() []string {
|
||||
if p.typedPlugins == nil {
|
||||
return nil
|
||||
}
|
||||
list := []string{}
|
||||
for k := range p.typedPlugins {
|
||||
list = append(list, k)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// FuncsByType implements server.TypedFunctionExporter
|
||||
func (p *Metadata) FuncsByType(t string) []template.Function {
|
||||
if p.typedPlugins == nil {
|
||||
return nil
|
||||
}
|
||||
fp, has := p.typedPlugins[t]
|
||||
if !has {
|
||||
return nil
|
||||
}
|
||||
exp, is := fp.(template.FunctionExporter)
|
||||
if !is {
|
||||
return nil
|
||||
}
|
||||
return exp.Funcs()
|
||||
}
|
||||
|
||||
// ImplementedInterface returns the interface implemented by this RPC service.
|
||||
func (p *Metadata) ImplementedInterface() spi.InterfaceSpec {
|
||||
return metadata.InterfaceSpec
|
||||
}
|
||||
|
||||
func (p *Metadata) getPlugin(metadataType string) metadata.Plugin {
|
||||
if metadataType == "" {
|
||||
return p.plugin
|
||||
}
|
||||
if p, has := p.typedPlugins[metadataType]; has {
|
||||
return p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// List returns a list of child nodes given a path.
|
||||
func (p *Metadata) List(_ *http.Request, req *ListRequest, resp *ListResponse) error {
|
||||
nodes := []string{}
|
||||
|
||||
// the . case - list the typed plugins and the default's first level.
|
||||
if len(req.Path) == 0 || req.Path[0] == "" || req.Path[0] == "." {
|
||||
if p.plugin != nil {
|
||||
n, err := p.plugin.List(req.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nodes = append(nodes, n...)
|
||||
}
|
||||
for k := range p.typedPlugins {
|
||||
nodes = append(nodes, k)
|
||||
}
|
||||
sort.Strings(nodes)
|
||||
resp.Nodes = nodes
|
||||
return nil
|
||||
}
|
||||
|
||||
c, has := p.typedPlugins[req.Path[0]]
|
||||
if !has {
|
||||
|
||||
if p.plugin == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
nodes, err := p.plugin.List(req.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sort.Strings(nodes)
|
||||
resp.Nodes = nodes
|
||||
return nil
|
||||
}
|
||||
|
||||
nodes, err := c.List(req.Path[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sort.Strings(nodes)
|
||||
resp.Nodes = nodes
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get retrieves the value at path given.
|
||||
func (p *Metadata) Get(_ *http.Request, req *GetRequest, resp *GetResponse) error {
|
||||
if len(req.Path) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
c, has := p.typedPlugins[req.Path[0]]
|
||||
if !has {
|
||||
|
||||
if p.plugin == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := p.plugin.Get(req.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Value = value
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := c.Get(req.Path[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Value = value
|
||||
return nil
|
||||
}
|
||||
26
vendor/github.com/docker/infrakit/pkg/rpc/metadata/types.go
generated
vendored
Normal file
26
vendor/github.com/docker/infrakit/pkg/rpc/metadata/types.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"github.com/docker/infrakit/pkg/spi/metadata"
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
// ListRequest is the rpc wrapper for request parameters to List
|
||||
type ListRequest struct {
|
||||
Path metadata.Path
|
||||
}
|
||||
|
||||
// ListResponse is the rpc wrapper for the results of List
|
||||
type ListResponse struct {
|
||||
Nodes []string
|
||||
}
|
||||
|
||||
// GetRequest is the rpc wrapper of the params to Get
|
||||
type GetRequest struct {
|
||||
Path metadata.Path
|
||||
}
|
||||
|
||||
// GetResponse is the rpc wrapper of the result of Get
|
||||
type GetResponse struct {
|
||||
Value *types.Any
|
||||
}
|
||||
134
vendor/github.com/docker/infrakit/pkg/rpc/server/info.go
generated
vendored
Normal file
134
vendor/github.com/docker/infrakit/pkg/rpc/server/info.go
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/infrakit/pkg/plugin"
|
||||
"github.com/docker/infrakit/pkg/spi"
|
||||
"github.com/docker/infrakit/pkg/template"
|
||||
)
|
||||
|
||||
// TypedFunctionExporter is an interface implemented by plugins that supports multiple types in a single RPC endpoint.
|
||||
// Each typed plugin can export some functions and this interface provides metadata about them.
|
||||
type TypedFunctionExporter interface {
|
||||
|
||||
// Types returns a list of types in this plugin
|
||||
Types() []string
|
||||
|
||||
// FuncsByType returns the template functions exported by each typed plugin
|
||||
FuncsByType(string) []template.Function
|
||||
}
|
||||
|
||||
// PluginInfo is the service object for the RPC metadata service
|
||||
type PluginInfo struct {
|
||||
vendor spi.Vendor
|
||||
reflectors []*reflector
|
||||
receiver interface{}
|
||||
}
|
||||
|
||||
// NewPluginInfo returns an instance of the metadata service
|
||||
func NewPluginInfo(receiver interface{}) (*PluginInfo, error) {
|
||||
m := &PluginInfo{
|
||||
reflectors: []*reflector{},
|
||||
receiver: receiver,
|
||||
}
|
||||
if v, is := receiver.(spi.Vendor); is {
|
||||
m.vendor = v
|
||||
}
|
||||
return m, m.Register(receiver)
|
||||
}
|
||||
|
||||
// Register registers an rpc-capable object for introspection and metadata service
|
||||
func (m *PluginInfo) Register(receiver interface{}) error {
|
||||
r := &reflector{target: receiver}
|
||||
m.reflectors = append(m.reflectors, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShowAPI responds by returning information about the plugin.
|
||||
func (m *PluginInfo) ShowAPI(resp http.ResponseWriter, req *http.Request) {
|
||||
meta := m.getInfo()
|
||||
buff, err := json.Marshal(meta)
|
||||
if err != nil {
|
||||
resp.WriteHeader(http.StatusInternalServerError)
|
||||
resp.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
resp.Write(buff)
|
||||
return
|
||||
}
|
||||
|
||||
// ShowTemplateFunctions responds by returning information about template functions the plugin expopses.
|
||||
func (m *PluginInfo) ShowTemplateFunctions(resp http.ResponseWriter, req *http.Request) {
|
||||
result := map[string][]template.Function{}
|
||||
exporter, is := m.receiver.(template.FunctionExporter)
|
||||
if is {
|
||||
base := template.UpdateDocumentation(exporter.Funcs())
|
||||
if len(base) > 0 {
|
||||
result["base"] = base
|
||||
}
|
||||
}
|
||||
|
||||
texporter, is := m.receiver.(TypedFunctionExporter)
|
||||
if is {
|
||||
for _, t := range texporter.Types() {
|
||||
typed := template.UpdateDocumentation(texporter.FuncsByType(t))
|
||||
if len(typed) > 0 {
|
||||
result[t] = typed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if t, err := template.NewTemplate("str://", template.Options{}); err == nil {
|
||||
builtin := template.UpdateDocumentation(t.DefaultFuncs())
|
||||
if len(builtin) > 0 {
|
||||
result["builtin"] = builtin
|
||||
}
|
||||
}
|
||||
|
||||
buff, err := json.MarshalIndent(result, "", " ")
|
||||
if err != nil {
|
||||
resp.WriteHeader(http.StatusInternalServerError)
|
||||
resp.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
resp.Write(buff)
|
||||
return
|
||||
}
|
||||
|
||||
func (m *PluginInfo) getInfo() *plugin.Info {
|
||||
meta := &plugin.Info{}
|
||||
myImplements := []spi.InterfaceSpec{}
|
||||
myInterfaces := []plugin.InterfaceDescription{}
|
||||
|
||||
for _, r := range m.reflectors {
|
||||
|
||||
iface := r.Interface()
|
||||
myImplements = append(myImplements, iface)
|
||||
|
||||
descriptions := []plugin.MethodDescription{}
|
||||
for _, method := range r.pluginMethods() {
|
||||
|
||||
desc := r.toDescription(method)
|
||||
descriptions = append(descriptions, desc)
|
||||
|
||||
// sets the example properties which are the custom types for the plugin
|
||||
r.setExampleProperties(desc.Request.Params)
|
||||
}
|
||||
|
||||
myInterfaces = append(myInterfaces,
|
||||
plugin.InterfaceDescription{
|
||||
InterfaceSpec: iface,
|
||||
Methods: descriptions,
|
||||
})
|
||||
}
|
||||
|
||||
if m.vendor != nil {
|
||||
meta.Vendor = m.vendor.VendorInfo()
|
||||
}
|
||||
|
||||
meta.Implements = myImplements
|
||||
meta.Interfaces = myInterfaces
|
||||
return meta
|
||||
}
|
||||
145
vendor/github.com/docker/infrakit/pkg/rpc/server/reflector.go
generated
vendored
Normal file
145
vendor/github.com/docker/infrakit/pkg/rpc/server/reflector.go
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/docker/infrakit/pkg/plugin"
|
||||
"github.com/docker/infrakit/pkg/rpc"
|
||||
"github.com/docker/infrakit/pkg/spi"
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// Precompute the reflect.Type of error and http.Request -- from gorilla/rpc
|
||||
typeOfError = reflect.TypeOf((*error)(nil)).Elem()
|
||||
typeOfHTTPRequest = reflect.TypeOf((*http.Request)(nil)).Elem()
|
||||
)
|
||||
|
||||
type reflector struct {
|
||||
target interface{}
|
||||
}
|
||||
|
||||
func (r *reflector) VendorInfo() *spi.VendorInfo {
|
||||
if i, is := r.target.(spi.Vendor); is {
|
||||
return i.VendorInfo()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reflector) exampleProperties() *types.Any {
|
||||
if example, is := r.target.(spi.InputExample); is {
|
||||
return example.ExampleProperties()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns the target's type, taking into account of pointer receiver
|
||||
func (r *reflector) targetType() reflect.Type {
|
||||
return reflect.Indirect(reflect.ValueOf(r.target)).Type()
|
||||
}
|
||||
|
||||
// Interface returns the plugin type and version.
|
||||
func (r *reflector) Interface() spi.InterfaceSpec {
|
||||
if v, is := r.target.(VersionedInterface); is {
|
||||
return v.ImplementedInterface()
|
||||
}
|
||||
return spi.InterfaceSpec{}
|
||||
}
|
||||
|
||||
// isExported returns true of a string is an exported (upper case) name. -- from gorilla/rpc
|
||||
func isExported(name string) bool {
|
||||
rune, _ := utf8.DecodeRuneInString(name)
|
||||
return unicode.IsUpper(rune)
|
||||
}
|
||||
|
||||
// isExportedOrBuiltin returns true if a type is exported or a builtin -- from gorilla/rpc
|
||||
func isExportedOrBuiltin(t reflect.Type) bool {
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
// PkgPath will be non-empty even for an exported type,
|
||||
// so we need to check the type name as well.
|
||||
return isExported(t.Name()) || t.PkgPath() == ""
|
||||
}
|
||||
|
||||
func (r *reflector) getPluginTypeName() string {
|
||||
return r.targetType().Name()
|
||||
}
|
||||
|
||||
func (r *reflector) setExampleProperties(param interface{}) {
|
||||
if example, is := r.target.(rpc.InputExample); is {
|
||||
example.SetExampleProperties(param)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reflector) toDescription(m reflect.Method) plugin.MethodDescription {
|
||||
method := fmt.Sprintf("%s.%s", r.getPluginTypeName(), m.Name)
|
||||
input := reflect.New(m.Type.In(2).Elem())
|
||||
ts := fmt.Sprintf("%v", time.Now().Unix())
|
||||
d := plugin.MethodDescription{
|
||||
|
||||
Request: plugin.Request{
|
||||
Version: "2.0",
|
||||
Method: method,
|
||||
Params: input.Interface(),
|
||||
ID: ts,
|
||||
},
|
||||
|
||||
Response: plugin.Response{
|
||||
Result: reflect.Zero(m.Type.In(3).Elem()).Interface(),
|
||||
ID: ts,
|
||||
},
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// pluginMethods returns a slice of methods that match the criteria for exporting as RPC service
|
||||
func (r *reflector) pluginMethods() []reflect.Method {
|
||||
matches := []reflect.Method{}
|
||||
receiverT := reflect.TypeOf(r.target)
|
||||
for i := 0; i < receiverT.NumMethod(); i++ {
|
||||
|
||||
method := receiverT.Method(i)
|
||||
mtype := method.Type
|
||||
|
||||
// Code from gorilla/rpc
|
||||
// Method must be exported.
|
||||
if method.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
// Method needs four ins: receiver, *http.Request, *args, *reply.
|
||||
if mtype.NumIn() != 4 {
|
||||
continue
|
||||
}
|
||||
// First argument must be a pointer and must be http.Request.
|
||||
reqType := mtype.In(1)
|
||||
if reqType.Kind() != reflect.Ptr || reqType.Elem() != typeOfHTTPRequest {
|
||||
continue
|
||||
}
|
||||
// Second argument must be a pointer and must be exported.
|
||||
args := mtype.In(2)
|
||||
if args.Kind() != reflect.Ptr || !isExportedOrBuiltin(args) {
|
||||
continue
|
||||
}
|
||||
// Third argument must be a pointer and must be exported.
|
||||
reply := mtype.In(3)
|
||||
if reply.Kind() != reflect.Ptr || !isExportedOrBuiltin(reply) {
|
||||
continue
|
||||
}
|
||||
// Method needs one out: error.
|
||||
if mtype.NumOut() != 1 {
|
||||
continue
|
||||
}
|
||||
if returnType := mtype.Out(0); returnType != typeOfError {
|
||||
continue
|
||||
}
|
||||
|
||||
matches = append(matches, method)
|
||||
}
|
||||
return matches
|
||||
}
|
||||
177
vendor/github.com/docker/infrakit/pkg/rpc/server/server.go
generated
vendored
Normal file
177
vendor/github.com/docker/infrakit/pkg/rpc/server/server.go
generated
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/http/httputil"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
broker "github.com/docker/infrakit/pkg/broker/server"
|
||||
rpc_server "github.com/docker/infrakit/pkg/rpc"
|
||||
"github.com/docker/infrakit/pkg/spi"
|
||||
"github.com/docker/infrakit/pkg/spi/event"
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/rpc/v2"
|
||||
"github.com/gorilla/rpc/v2/json2"
|
||||
"gopkg.in/tylerb/graceful.v1"
|
||||
)
|
||||
|
||||
// Stoppable support proactive stopping, and blocking until stopped.
|
||||
type Stoppable interface {
|
||||
Stop()
|
||||
AwaitStopped()
|
||||
}
|
||||
|
||||
type stoppableServer struct {
|
||||
server *graceful.Server
|
||||
}
|
||||
|
||||
func (s *stoppableServer) Stop() {
|
||||
s.server.Stop(10 * time.Second)
|
||||
}
|
||||
|
||||
func (s *stoppableServer) AwaitStopped() {
|
||||
<-s.server.StopChan()
|
||||
}
|
||||
|
||||
type loggingHandler struct {
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
requestData, err := httputil.DumpRequest(req, true)
|
||||
if err == nil {
|
||||
log.Debugf("Received request %s", string(requestData))
|
||||
} else {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
h.handler.ServeHTTP(recorder, req)
|
||||
|
||||
responseData, err := httputil.DumpResponse(recorder.Result(), true)
|
||||
if err == nil {
|
||||
log.Debugf("Sending response %s", string(responseData))
|
||||
} else {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
w.WriteHeader(recorder.Code)
|
||||
recorder.Body.WriteTo(w)
|
||||
}
|
||||
|
||||
// A VersionedInterface identifies which Interfaces a plugin supports.
|
||||
type VersionedInterface interface {
|
||||
// ImplementedInterface returns the interface being provided.
|
||||
ImplementedInterface() spi.InterfaceSpec
|
||||
}
|
||||
|
||||
// StartPluginAtPath starts an HTTP server listening on a unix socket at the specified path.
|
||||
// Returns a Stoppable that can be used to stop or block on the server.
|
||||
func StartPluginAtPath(socketPath string, receiver VersionedInterface, more ...VersionedInterface) (Stoppable, error) {
|
||||
server := rpc.NewServer()
|
||||
server.RegisterCodec(json2.NewCodec(), "application/json")
|
||||
|
||||
targets := append([]VersionedInterface{receiver}, more...)
|
||||
|
||||
interfaces := []spi.InterfaceSpec{}
|
||||
for _, t := range targets {
|
||||
interfaces = append(interfaces, t.ImplementedInterface())
|
||||
|
||||
if err := server.RegisterService(t, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// handshake service that can exchange interface versions with client
|
||||
if err := server.RegisterService(rpc_server.Handshake(interfaces), ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// events handler
|
||||
events := broker.NewBroker()
|
||||
|
||||
// wire up the publish event source channel to the plugin implementations
|
||||
for _, t := range targets {
|
||||
|
||||
pub, is := t.(event.Publisher)
|
||||
if !is {
|
||||
continue
|
||||
}
|
||||
|
||||
// We give one channel per source to provide some isolation. This we won't have the
|
||||
// whole event bus stop just because one plugin closes the channel.
|
||||
eventChan := make(chan *event.Event)
|
||||
pub.PublishOn(eventChan)
|
||||
go func() {
|
||||
for {
|
||||
event, ok := <-eventChan
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
events.Publish(event.Topic.String(), event, 1*time.Second)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// info handler
|
||||
info, err := NewPluginInfo(receiver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
httpLog := log.New()
|
||||
httpLog.Level = log.GetLevel()
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc(rpc_server.URLAPI, info.ShowAPI)
|
||||
router.HandleFunc(rpc_server.URLFunctions, info.ShowTemplateFunctions)
|
||||
|
||||
intercept := broker.Interceptor{
|
||||
Pre: func(topic string, headers map[string][]string) error {
|
||||
for _, target := range targets {
|
||||
if v, is := target.(event.Validator); is {
|
||||
if err := v.Validate(types.PathFromString(topic)); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return broker.ErrInvalidTopic(topic)
|
||||
},
|
||||
Do: events.ServeHTTP,
|
||||
Post: func(topic string) {
|
||||
log.Infoln("Client left", topic)
|
||||
},
|
||||
}
|
||||
router.HandleFunc(rpc_server.URLEventsPrefix, intercept.ServeHTTP)
|
||||
|
||||
logger := loggingHandler{handler: server}
|
||||
router.Handle("/", logger)
|
||||
|
||||
gracefulServer := graceful.Server{
|
||||
Timeout: 10 * time.Second,
|
||||
Server: &http.Server{Addr: fmt.Sprintf("unix://%s", socketPath), Handler: router},
|
||||
}
|
||||
|
||||
listener, err := net.Listen("unix", socketPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Infof("Listening at: %s", socketPath)
|
||||
|
||||
go func() {
|
||||
err := gracefulServer.Serve(listener)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
events.Stop()
|
||||
}()
|
||||
|
||||
return &stoppableServer{server: &gracefulServer}, nil
|
||||
}
|
||||
42
vendor/github.com/docker/infrakit/pkg/spi/event/spi.go
generated
vendored
Normal file
42
vendor/github.com/docker/infrakit/pkg/spi/event/spi.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"github.com/docker/infrakit/pkg/spi"
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
// InterfaceSpec is the current name and version of the Flavor API.
|
||||
var InterfaceSpec = spi.InterfaceSpec{
|
||||
Name: "Event",
|
||||
Version: "0.1.0",
|
||||
}
|
||||
|
||||
// Plugin must be implemented for the object to be able to publish events.
|
||||
type Plugin interface {
|
||||
|
||||
// List returns a list of *child nodes* given a path for a topic.
|
||||
// A topic of "." is the top level
|
||||
List(topic types.Path) (child []string, err error)
|
||||
}
|
||||
|
||||
// Validator is the interface for validating the topic
|
||||
type Validator interface {
|
||||
|
||||
// Validate validates the topic
|
||||
Validate(topic types.Path) error
|
||||
}
|
||||
|
||||
// Publisher is the interface that event sources also implement to be assigned
|
||||
// a publish function.
|
||||
type Publisher interface {
|
||||
|
||||
// PublishOn sets the channel to publish
|
||||
PublishOn(chan<- *Event)
|
||||
}
|
||||
|
||||
// Subscriber is the interface given to clients interested in events
|
||||
type Subscriber interface {
|
||||
|
||||
// SubscribeOn returns the channel for the topic
|
||||
SubscribeOn(topic types.Path) (<-chan *Event, error)
|
||||
}
|
||||
105
vendor/github.com/docker/infrakit/pkg/spi/event/types.go
generated
vendored
Normal file
105
vendor/github.com/docker/infrakit/pkg/spi/event/types.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
// Type is the type of an event. This gives hint about what struct types to map to, etc.
|
||||
// It also marks one instance of an event as of different nature from another.
|
||||
type Type string
|
||||
|
||||
const (
|
||||
// TypeError is the type to use for sending errors in the transport of the events.
|
||||
TypeError = Type("error")
|
||||
)
|
||||
|
||||
// Event holds information about when, what, etc.
|
||||
type Event struct {
|
||||
|
||||
// Topic is the topic to which this event is published
|
||||
Topic types.Path
|
||||
|
||||
// Type of the event. This is usually used as a hint to what struct types to use to unmarshal data
|
||||
Type Type `json:",omitempty"`
|
||||
|
||||
// ID is unique id for the event.
|
||||
ID string
|
||||
|
||||
// Timestamp is the time.UnixNano() value -- this is the timestamp when event occurred
|
||||
Timestamp time.Time
|
||||
|
||||
// Received is the timestamp when the message is received.
|
||||
Received time.Time `json:",omitempty"`
|
||||
|
||||
// Data contains some application specific payload
|
||||
Data *types.Any `json:",omitempty"`
|
||||
|
||||
// Error contains any errors that occurred during delivery of the mesasge
|
||||
Error error `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Init creates an instance with the value initialized to the state of receiver.
|
||||
func (event Event) Init() *Event {
|
||||
copy := event
|
||||
return ©
|
||||
}
|
||||
|
||||
// WithError sets the error
|
||||
func (event *Event) WithError(err error) *Event {
|
||||
event.Error = err
|
||||
return event
|
||||
}
|
||||
|
||||
// WithTopic sets the topic from input string
|
||||
func (event *Event) WithTopic(s string) *Event {
|
||||
event.Topic = types.PathFromString(s)
|
||||
return event
|
||||
}
|
||||
|
||||
// WithType sets the type from a string
|
||||
func (event *Event) WithType(s string) *Event {
|
||||
event.Type = Type(s)
|
||||
return event
|
||||
}
|
||||
|
||||
// Now sets the timestamp of this event to now.
|
||||
func (event *Event) Now() *Event {
|
||||
event.Timestamp = time.Now()
|
||||
return event
|
||||
}
|
||||
|
||||
// ReceivedNow marks the receipt timestamp with now
|
||||
func (event *Event) ReceivedNow() *Event {
|
||||
event.Received = time.Now()
|
||||
return event
|
||||
}
|
||||
|
||||
// WithData converts the data into an any and sets this event's data to it.
|
||||
func (event *Event) WithData(data interface{}) (*Event, error) {
|
||||
any, err := types.AnyValue(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
event.Data = any
|
||||
return event, nil
|
||||
}
|
||||
|
||||
// WithDataMust does what WithData does but will panic on error
|
||||
func (event *Event) WithDataMust(data interface{}) *Event {
|
||||
e, err := event.WithData(data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// FromAny sets the fields of this Event from the contents of Any
|
||||
func (event *Event) FromAny(any *types.Any) *Event {
|
||||
err := any.Decode(event)
|
||||
if err != nil {
|
||||
event.Error = err
|
||||
}
|
||||
return event
|
||||
}
|
||||
30
vendor/github.com/docker/infrakit/pkg/spi/instance/spi.go
generated
vendored
Normal file
30
vendor/github.com/docker/infrakit/pkg/spi/instance/spi.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package instance
|
||||
|
||||
import (
|
||||
"github.com/docker/infrakit/pkg/spi"
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
// InterfaceSpec is the current name and version of the Instance API.
|
||||
var InterfaceSpec = spi.InterfaceSpec{
|
||||
Name: "Instance",
|
||||
Version: "0.3.0",
|
||||
}
|
||||
|
||||
// Plugin is a vendor-agnostic API used to create and manage resources with an infrastructure provider.
|
||||
type Plugin interface {
|
||||
// Validate performs local validation on a provision request.
|
||||
Validate(req *types.Any) error
|
||||
|
||||
// Provision creates a new instance based on the spec.
|
||||
Provision(spec Spec) (*ID, error)
|
||||
|
||||
// Label labels the instance
|
||||
Label(instance ID, labels map[string]string) error
|
||||
|
||||
// Destroy terminates an existing instance.
|
||||
Destroy(instance ID) error
|
||||
|
||||
// DescribeInstances returns descriptions of all instances matching all of the provided tags.
|
||||
DescribeInstances(labels map[string]string) ([]Description, error)
|
||||
}
|
||||
46
vendor/github.com/docker/infrakit/pkg/spi/instance/types.go
generated
vendored
Normal file
46
vendor/github.com/docker/infrakit/pkg/spi/instance/types.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
package instance
|
||||
|
||||
import (
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
// ID is the identifier for an instance.
|
||||
type ID string
|
||||
|
||||
// Description contains details about an instance.
|
||||
type Description struct {
|
||||
ID ID
|
||||
LogicalID *LogicalID
|
||||
Tags map[string]string
|
||||
}
|
||||
|
||||
// LogicalID is the logical identifier to associate with an instance.
|
||||
type LogicalID string
|
||||
|
||||
// Attachment is an identifier for a resource to attach to an instance.
|
||||
type Attachment struct {
|
||||
// ID is the unique identifier for the attachment.
|
||||
ID string
|
||||
|
||||
// Type is the kind of attachment. This allows multiple attachments of different types, with the supported
|
||||
// types defined by the plugin.
|
||||
Type string
|
||||
}
|
||||
|
||||
// Spec is a specification of an instance to be provisioned
|
||||
type Spec struct {
|
||||
// Properties is the opaque instance plugin configuration.
|
||||
Properties *types.Any
|
||||
|
||||
// Tags are metadata that describes an instance.
|
||||
Tags map[string]string
|
||||
|
||||
// Init is the boot script to execute when the instance is created.
|
||||
Init string
|
||||
|
||||
// LogicalID is the logical identifier assigned to this instance, which may be absent.
|
||||
LogicalID *LogicalID
|
||||
|
||||
// Attachments are instructions for external entities that should be attached to the instance.
|
||||
Attachments []Attachment
|
||||
}
|
||||
102
vendor/github.com/docker/infrakit/pkg/spi/metadata/path.go
generated
vendored
Normal file
102
vendor/github.com/docker/infrakit/pkg/spi/metadata/path.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
package metadata
|
||||
|
||||
var (
|
||||
// NullPath means no path
|
||||
NullPath = Path([]string{})
|
||||
)
|
||||
|
||||
// Path is used to identify a particle of metadata. The path can be strings separated by / as in a URL.
|
||||
type Path []string
|
||||
|
||||
// Clean scrubs the path to remove any empty string or . or .. and collapse the path into a concise form.
|
||||
// It's similar to path/filepath.Clean in the standard lib.
|
||||
func (p Path) Clean() Path {
|
||||
this := []string(p)
|
||||
copy := []string{}
|
||||
for _, v := range this {
|
||||
switch v {
|
||||
case "", ".":
|
||||
case "..":
|
||||
if len(copy) == 0 {
|
||||
copy = append(copy, "..")
|
||||
} else {
|
||||
copy = copy[0 : len(copy)-1]
|
||||
if len(copy) == 0 {
|
||||
return NullPath
|
||||
}
|
||||
}
|
||||
default:
|
||||
copy = append(copy, v)
|
||||
|
||||
}
|
||||
}
|
||||
return Path(copy)
|
||||
}
|
||||
|
||||
// Len returns the length of the path
|
||||
func (p Path) Len() int {
|
||||
return len([]string(p))
|
||||
}
|
||||
|
||||
// Index returns the ith component in the path
|
||||
func (p Path) Index(i int) *string {
|
||||
if p.Len() <= i {
|
||||
return nil
|
||||
}
|
||||
copy := []string(p)[i]
|
||||
return ©
|
||||
}
|
||||
|
||||
// Shift returns a new path that's shifted i positions to the left -- ith child of the head at index=0
|
||||
func (p Path) Shift(i int) Path {
|
||||
len := p.Len() - i
|
||||
if len <= 0 {
|
||||
return Path([]string{})
|
||||
}
|
||||
new := make([]string, len)
|
||||
copy(new, []string(p)[i:])
|
||||
return Path(new)
|
||||
}
|
||||
|
||||
// Dir returns the 'dir' of the path
|
||||
func (p Path) Dir() Path {
|
||||
pp := p.Clean()
|
||||
if len(pp) > 1 {
|
||||
return p[0 : len(pp)-1]
|
||||
}
|
||||
return Path([]string{"."})
|
||||
}
|
||||
|
||||
// Base returns the base of the path
|
||||
func (p Path) Base() string {
|
||||
pp := p.Clean()
|
||||
return pp[len(pp)-1]
|
||||
}
|
||||
|
||||
// Join joins the input as a child of this path
|
||||
func (p Path) Join(child string) Path {
|
||||
return p.Sub(Path([]string{child}))
|
||||
}
|
||||
|
||||
// Sub joins the child to the parent
|
||||
func (p Path) Sub(child Path) Path {
|
||||
pp := p.Clean()
|
||||
return Path(append(pp, []string(child)...))
|
||||
}
|
||||
|
||||
// Rel returns a new path that is a child of the input from this path.
|
||||
// e.g. For a path a/b/c/d Rel(a/b/) returns c/d. NullPath is returned if
|
||||
// the two are not relative to one another.
|
||||
func (p Path) Rel(path Path) Path {
|
||||
this := []string(p.Clean())
|
||||
parent := []string(path.Clean())
|
||||
if len(this) < len(parent) {
|
||||
return NullPath
|
||||
}
|
||||
for i := 0; i < len(parent); i++ {
|
||||
if parent[i] != this[i] {
|
||||
return NullPath
|
||||
}
|
||||
}
|
||||
return Path(this[len(parent):])
|
||||
}
|
||||
22
vendor/github.com/docker/infrakit/pkg/spi/metadata/spi.go
generated
vendored
Normal file
22
vendor/github.com/docker/infrakit/pkg/spi/metadata/spi.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"github.com/docker/infrakit/pkg/spi"
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
// InterfaceSpec is the current name and version of the Metadata API.
|
||||
var InterfaceSpec = spi.InterfaceSpec{
|
||||
Name: "Metadata",
|
||||
Version: "0.1.0",
|
||||
}
|
||||
|
||||
// Plugin is the interface for metadata-related operations.
|
||||
type Plugin interface {
|
||||
|
||||
// List returns a list of *child nodes* given a path, which is specified as a slice
|
||||
List(path Path) (child []string, err error)
|
||||
|
||||
// Get retrieves the value at path given.
|
||||
Get(path Path) (value *types.Any, err error)
|
||||
}
|
||||
39
vendor/github.com/docker/infrakit/pkg/spi/plugin.go
generated
vendored
Normal file
39
vendor/github.com/docker/infrakit/pkg/spi/plugin.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package spi
|
||||
|
||||
import (
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
)
|
||||
|
||||
// InterfaceSpec is metadata about an API.
|
||||
type InterfaceSpec struct {
|
||||
// Name of the interface.
|
||||
Name string
|
||||
|
||||
// Version is the identifier for the API version.
|
||||
Version string
|
||||
}
|
||||
|
||||
// VendorInfo provides vendor-specific information
|
||||
type VendorInfo struct {
|
||||
InterfaceSpec // vendor-defined name / version
|
||||
|
||||
// URL is the informational url for the plugin. It can container help and docs, etc.
|
||||
URL string
|
||||
}
|
||||
|
||||
// Vendor is an optional interface that has vendor-specific information methods
|
||||
type Vendor interface {
|
||||
// VendorInfo returns a vendor-defined interface spec
|
||||
VendorInfo() *VendorInfo
|
||||
}
|
||||
|
||||
// InputExample interface is an optional interface implemented by the plugin that will provide
|
||||
// example input struct to document the vendor-specific api of the plugin. An example of this
|
||||
// is to provide a sample JSON for all the Properties field in the plugin API.
|
||||
type InputExample interface {
|
||||
|
||||
// ExampleProperties returns an example JSON raw message that the vendor plugin understands.
|
||||
// This is an example of what the user will configure and what will be used as the opaque
|
||||
// blob in all the plugin methods where raw JSON messages are referenced.
|
||||
ExampleProperties() *types.Any
|
||||
}
|
||||
16
vendor/github.com/docker/infrakit/pkg/template/defaults.go
generated
vendored
Normal file
16
vendor/github.com/docker/infrakit/pkg/template/defaults.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// defaultContextURL returns the default context URL if none is known.
|
||||
func defaultContextURL() string {
|
||||
pwd := "/"
|
||||
if wd, err := os.Getwd(); err == nil {
|
||||
pwd = wd
|
||||
} else {
|
||||
pwd = os.Getenv("PWD")
|
||||
}
|
||||
return "file://localhost" + pwd + "/"
|
||||
}
|
||||
64
vendor/github.com/docker/infrakit/pkg/template/fetch.go
generated
vendored
Normal file
64
vendor/github.com/docker/infrakit/pkg/template/fetch.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Fetch fetchs content from the given URL string. Supported schemes are http:// https:// file:// unix://
|
||||
func Fetch(s string, opt Options) ([]byte, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch u.Scheme {
|
||||
case "file":
|
||||
return ioutil.ReadFile(u.Path)
|
||||
|
||||
case "http", "https":
|
||||
resp, err := http.Get(u.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return ioutil.ReadAll(resp.Body)
|
||||
|
||||
case "unix":
|
||||
// unix: will look for a socket that matches the host name at a
|
||||
// directory path set by environment variable.
|
||||
c, err := socketClient(u, opt.SocketDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scheme = "http"
|
||||
resp, err := c.Get(u.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return ioutil.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported url:%s", s)
|
||||
}
|
||||
|
||||
func socketClient(u *url.URL, socketDir string) (*http.Client, error) {
|
||||
socketPath := filepath.Join(socketDir, u.Host)
|
||||
if f, err := os.Stat(socketPath); err != nil {
|
||||
return nil, err
|
||||
} else if f.Mode()&os.ModeSocket == 0 {
|
||||
return nil, fmt.Errorf("not-a-socket:%v", socketPath)
|
||||
}
|
||||
return &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Dial: func(proto, addr string) (conn net.Conn, err error) {
|
||||
return net.Dial("unix", socketPath)
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
361
vendor/github.com/docker/infrakit/pkg/template/funcs.go
generated
vendored
Normal file
361
vendor/github.com/docker/infrakit/pkg/template/funcs.go
generated
vendored
Normal file
@@ -0,0 +1,361 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/infrakit/pkg/types"
|
||||
"github.com/jmespath/go-jmespath"
|
||||
)
|
||||
|
||||
// DeepCopyObject makes a deep copy of the argument, using encoding/gob encode/decode.
|
||||
func DeepCopyObject(from interface{}) (interface{}, error) {
|
||||
var mod bytes.Buffer
|
||||
enc := json.NewEncoder(&mod)
|
||||
dec := json.NewDecoder(&mod)
|
||||
err := enc.Encode(from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
copy := reflect.New(reflect.TypeOf(from))
|
||||
err = dec.Decode(copy.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return reflect.Indirect(copy).Interface(), nil
|
||||
}
|
||||
|
||||
// QueryObject applies a JMESPath query specified by the expression, against the target object.
|
||||
func QueryObject(exp string, target interface{}) (interface{}, error) {
|
||||
query, err := jmespath.Compile(exp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.Search(target)
|
||||
}
|
||||
|
||||
// SplitLines splits the input into a string slice.
|
||||
func SplitLines(o interface{}) ([]string, error) {
|
||||
ret := []string{}
|
||||
switch o := o.(type) {
|
||||
case string:
|
||||
return strings.Split(o, "\n"), nil
|
||||
case []byte:
|
||||
return strings.Split(string(o), "\n"), nil
|
||||
}
|
||||
return ret, fmt.Errorf("not-supported-value-type")
|
||||
}
|
||||
|
||||
// FromJSON decode the input JSON encoded as string or byte slice into a map.
|
||||
func FromJSON(o interface{}) (interface{}, error) {
|
||||
var ret interface{}
|
||||
switch o := o.(type) {
|
||||
case string:
|
||||
err := json.Unmarshal([]byte(o), &ret)
|
||||
return ret, err
|
||||
case []byte:
|
||||
err := json.Unmarshal(o, &ret)
|
||||
return ret, err
|
||||
case *types.Any:
|
||||
err := json.Unmarshal(o.Bytes(), &ret)
|
||||
return ret, err
|
||||
}
|
||||
return ret, fmt.Errorf("not-supported-value-type")
|
||||
}
|
||||
|
||||
// ToJSON encodes the input struct into a JSON string.
|
||||
func ToJSON(o interface{}) (string, error) {
|
||||
buff, err := json.MarshalIndent(o, "", " ")
|
||||
return string(buff), err
|
||||
}
|
||||
|
||||
// ToJSONFormat encodes the input struct into a JSON string with format prefix, and indent.
|
||||
func ToJSONFormat(prefix, indent string, o interface{}) (string, error) {
|
||||
buff, err := json.MarshalIndent(o, prefix, indent)
|
||||
return string(buff), err
|
||||
}
|
||||
|
||||
// FromMap decodes map into raw struct
|
||||
func FromMap(m map[string]interface{}, raw interface{}) error {
|
||||
// The safest way, but the slowest, is to just marshal and unmarshal back
|
||||
buff, err := ToJSON(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal([]byte(buff), raw)
|
||||
}
|
||||
|
||||
// ToMap encodes the input as a map
|
||||
func ToMap(raw interface{}) (map[string]interface{}, error) {
|
||||
buff, err := ToJSON(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := FromJSON(buff)
|
||||
return out.(map[string]interface{}), err
|
||||
}
|
||||
|
||||
// UnixTime returns a timestamp in unix time
|
||||
func UnixTime() interface{} {
|
||||
return time.Now().Unix()
|
||||
}
|
||||
|
||||
// IndexOf returns the index of search in array. -1 if not found or array is not iterable. An optional true will
|
||||
// turn on strict type check while by default string representations are used to compare values.
|
||||
func IndexOf(srch interface{}, array interface{}, strictOptional ...bool) int {
|
||||
strict := false
|
||||
if len(strictOptional) > 0 {
|
||||
strict = strictOptional[0]
|
||||
}
|
||||
switch reflect.TypeOf(array).Kind() {
|
||||
case reflect.Slice:
|
||||
s := reflect.ValueOf(array)
|
||||
for i := 0; i < s.Len(); i++ {
|
||||
if reflect.DeepEqual(srch, s.Index(i).Interface()) {
|
||||
return i
|
||||
}
|
||||
if !strict {
|
||||
// by string value which is useful for text based compares
|
||||
search := reflect.Indirect(reflect.ValueOf(srch)).Interface()
|
||||
value := reflect.Indirect(s.Index(i)).Interface()
|
||||
searchStr := fmt.Sprintf("%v", search)
|
||||
check := fmt.Sprintf("%v", value)
|
||||
if searchStr == check {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// DefaultFuncs returns a list of default functions for binding in the template
|
||||
func (t *Template) DefaultFuncs() []Function {
|
||||
return []Function{
|
||||
{
|
||||
Name: "source",
|
||||
Description: []string{
|
||||
"Source / evaluate the template at the input location (as URL).",
|
||||
"This will make all of the global variables declared there visible in this template's context.",
|
||||
"Similar to 'source' in bash, sourcing another template means applying it in the same context ",
|
||||
"as the calling template. The context (e.g. variables) of the calling template as a result can be mutated.",
|
||||
},
|
||||
Func: func(p string, opt ...interface{}) (string, error) {
|
||||
var o interface{}
|
||||
if len(opt) > 0 {
|
||||
o = opt[0]
|
||||
}
|
||||
loc := p
|
||||
if strings.Index(loc, "str://") == -1 {
|
||||
buff, err := getURL(t.url, p)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
loc = buff
|
||||
}
|
||||
sourced, err := NewTemplate(loc, t.options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// set this as the parent of the sourced template so its global can mutate the globals in this
|
||||
sourced.parent = t
|
||||
sourced.forkFrom(t)
|
||||
sourced.context = t.context
|
||||
|
||||
if o == nil {
|
||||
o = sourced.context
|
||||
}
|
||||
// TODO(chungers) -- let the sourced template define new functions that can be called in the parent.
|
||||
return sourced.Render(o)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "include",
|
||||
Description: []string{
|
||||
"Render content found at URL as template and include here.",
|
||||
"The optional second parameter is the context to use when rendering the template.",
|
||||
"Conceptually similar to exec in bash, where the template included is applied using a fork ",
|
||||
"of current context in the calling template. Any mutations to the context via 'global' will not ",
|
||||
"be visible in the calling template's context.",
|
||||
},
|
||||
Func: func(p string, opt ...interface{}) (string, error) {
|
||||
var o interface{}
|
||||
if len(opt) > 0 {
|
||||
o = opt[0]
|
||||
}
|
||||
loc := p
|
||||
if strings.Index(loc, "str://") == -1 {
|
||||
buff, err := getURL(t.url, p)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
loc = buff
|
||||
}
|
||||
included, err := NewTemplate(loc, t.options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dotCopy, err := included.forkFrom(t)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
included.context = dotCopy
|
||||
|
||||
if o == nil {
|
||||
o = included.context
|
||||
}
|
||||
|
||||
return included.Render(o)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "loop",
|
||||
Description: []string{
|
||||
"Loop generates a slice of length specified by the input. For use like {{ range loop 5 }}...{{ end }}",
|
||||
},
|
||||
Func: func(c int) []struct{} {
|
||||
return make([]struct{}, c)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "global",
|
||||
Description: []string{
|
||||
"Sets a global variable named after the first argument, with the value as the second argument.",
|
||||
"This is similar to def (which sets the default value).",
|
||||
"Global variables are propagated to all templates that are rendered via the 'include' function.",
|
||||
},
|
||||
Func: func(n string, v interface{}) Void {
|
||||
t.Global(n, v)
|
||||
return voidValue
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "def",
|
||||
Description: []string{
|
||||
"Defines a variable with the first argument as name and last argument value as the default.",
|
||||
"It's also ok to pass a third optional parameter, in the middle, as the documentation string.",
|
||||
},
|
||||
Func: func(name string, args ...interface{}) (Void, error) {
|
||||
if _, has := t.defaults[name]; has {
|
||||
// not sure if this is good, but should complain loudly
|
||||
return voidValue, fmt.Errorf("already defined: %v", name)
|
||||
}
|
||||
var doc string
|
||||
var value interface{}
|
||||
switch len(args) {
|
||||
case 1:
|
||||
// just value, no docs
|
||||
value = args[0]
|
||||
case 2:
|
||||
// docs and value
|
||||
doc = fmt.Sprintf("%v", args[0])
|
||||
value = args[1]
|
||||
}
|
||||
t.Def(name, value, doc)
|
||||
return voidValue, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ref",
|
||||
Description: []string{
|
||||
"References / gets the variable named after the first argument.",
|
||||
"The values must be set first by either def or global.",
|
||||
},
|
||||
Func: t.Ref,
|
||||
},
|
||||
{
|
||||
Name: "q",
|
||||
Description: []string{
|
||||
"Runs a JMESPath (http://jmespath.org/) query (first arg) on the object (second arg).",
|
||||
"The return value is an object which needs to be rendered properly for the format of the document.",
|
||||
"Example: {{ include \"https://httpbin.org/get\" | from_json | q \"origin\" }} returns the origin of http request.",
|
||||
},
|
||||
Func: QueryObject,
|
||||
},
|
||||
{
|
||||
Name: "to_json",
|
||||
Description: []string{
|
||||
"Encodes the input as a JSON string",
|
||||
"This is useful for taking an object (interface{}) and render it inline as proper JSON.",
|
||||
"Example: {{ include \"https://httpbin.org/get\" | from_json | to_json }}",
|
||||
},
|
||||
Func: ToJSON,
|
||||
},
|
||||
{
|
||||
Name: "jsonEncode",
|
||||
Description: []string{
|
||||
"Encodes the input as a JSON string",
|
||||
"This is useful for taking an object (interface{}) and render it inline as proper JSON.",
|
||||
"Example: {{ include \"https://httpbin.org/get\" | from_json | to_json }}",
|
||||
},
|
||||
Func: ToJSON,
|
||||
},
|
||||
{
|
||||
Name: "to_json_format",
|
||||
Description: []string{
|
||||
"Encodes the input as a JSON string with first arg as prefix, second arg the indentation, then the object",
|
||||
},
|
||||
Func: ToJSONFormat,
|
||||
},
|
||||
{
|
||||
Name: "jsonEncodeIndent",
|
||||
Description: []string{
|
||||
"Encodes the input as a JSON string with first arg as prefix, second arg the indentation, then the object",
|
||||
},
|
||||
Func: ToJSONFormat,
|
||||
},
|
||||
{
|
||||
Name: "from_json",
|
||||
Description: []string{
|
||||
"Decodes the input (first arg) into a structure (a map[string]interface{} or []interface{}).",
|
||||
"This is useful for parsing arbitrary resources in JSON format as object. The object is the queryable via 'q'",
|
||||
"For example: {{ include \"https://httpbin.org/get\" | from_json | q \"origin\" }} returns the origin of request.",
|
||||
},
|
||||
Func: FromJSON,
|
||||
},
|
||||
{
|
||||
Name: "jsonDecode",
|
||||
Description: []string{
|
||||
"Decodes the input (first arg) into a structure (a map[string]interface{} or []interface{}).",
|
||||
"This is useful for parsing arbitrary resources in JSON format as object. The object is the queryable via 'q'",
|
||||
"For example: {{ include \"https://httpbin.org/get\" | from_json | q \"origin\" }} returns the origin of request.",
|
||||
},
|
||||
Func: FromJSON,
|
||||
},
|
||||
{
|
||||
Name: "unixtime",
|
||||
Description: []string{
|
||||
"Returns the unix timestamp as the number of seconds elapsed since January 1, 1970 UTC.",
|
||||
},
|
||||
Func: UnixTime,
|
||||
},
|
||||
{
|
||||
Name: "lines",
|
||||
Description: []string{
|
||||
"Splits the input string (first arg) into a slice by '\n'",
|
||||
},
|
||||
Func: SplitLines,
|
||||
},
|
||||
{
|
||||
Name: "index_of",
|
||||
Description: []string{
|
||||
"Returns the index of first argument in the second argument which is a slice.",
|
||||
"Example: {{ index_of \"foo\" (from_json \"[\"bar\",\"foo\",\"baz\"]\") }} returns 1 (int).",
|
||||
},
|
||||
Func: IndexOf,
|
||||
},
|
||||
{
|
||||
Name: "indexOf",
|
||||
Description: []string{
|
||||
"Returns the index of first argument in the second argument which is a slice.",
|
||||
"Example: {{ index_of \"foo\" (from_json \"[\"bar\",\"foo\",\"baz\"]\") }} returns 1 (int).",
|
||||
},
|
||||
Func: IndexOf,
|
||||
},
|
||||
}
|
||||
}
|
||||
83
vendor/github.com/docker/infrakit/pkg/template/help.go
generated
vendored
Normal file
83
vendor/github.com/docker/infrakit/pkg/template/help.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// UpdateDocumentation uses reflection to generate documentation on usage and function signature.
|
||||
func UpdateDocumentation(in []Function) []Function {
|
||||
out := []Function{}
|
||||
for _, f := range in {
|
||||
copy := f
|
||||
copy.Function = functionSignature(f.Name, f.Func)
|
||||
copy.Usage = functionUsage(f.Name, f.Func)
|
||||
if len(f.Description) == 0 {
|
||||
copy.Description = []string{"None"}
|
||||
}
|
||||
out = append(out, copy)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func isFunc(f interface{}) (string, bool) {
|
||||
if f == nil {
|
||||
return "no-function", false
|
||||
}
|
||||
|
||||
ft := reflect.TypeOf(f)
|
||||
if ft.Kind() != reflect.Func {
|
||||
return "not-a-function", false
|
||||
}
|
||||
return ft.String(), true
|
||||
}
|
||||
|
||||
func functionSignature(name string, f interface{}) string {
|
||||
s, is := isFunc(f)
|
||||
if !is {
|
||||
return s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func functionUsage(name string, f interface{}) string {
|
||||
if s, is := isFunc(f); !is {
|
||||
return s
|
||||
}
|
||||
|
||||
ft := reflect.TypeOf(f)
|
||||
if ft.Kind() != reflect.Func {
|
||||
return "not-a-function"
|
||||
}
|
||||
|
||||
args := make([]string, ft.NumIn())
|
||||
for i := 0; i < len(args); i++ {
|
||||
t := ft.In(i)
|
||||
|
||||
v := ""
|
||||
switch {
|
||||
case t == reflect.TypeOf(""):
|
||||
v = fmt.Sprintf("\"%s\"", t.Name())
|
||||
|
||||
case t.Kind() == reflect.Slice && i == len(args)-1:
|
||||
tt := t.Elem().Name()
|
||||
if t.Elem() == reflect.TypeOf("") {
|
||||
tt = fmt.Sprintf("\"%s\"", t.Name())
|
||||
}
|
||||
v = fmt.Sprintf("[ %s ... ]", tt)
|
||||
case t.String() == "interface {}":
|
||||
v = "any"
|
||||
default:
|
||||
v = strings.Replace(t.String(), " ", "", -1)
|
||||
}
|
||||
|
||||
args[i] = v
|
||||
}
|
||||
|
||||
arglist := strings.Join(args, " ")
|
||||
if len(arglist) > 0 {
|
||||
arglist = arglist + " "
|
||||
}
|
||||
return fmt.Sprintf("{{ %s %s}}", name, arglist)
|
||||
}
|
||||
385
vendor/github.com/docker/infrakit/pkg/template/template.go
generated
vendored
Normal file
385
vendor/github.com/docker/infrakit/pkg/template/template.go
generated
vendored
Normal file
@@ -0,0 +1,385 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
|
||||
"github.com/Masterminds/sprig"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Function contains the description of an exported template function
|
||||
type Function struct {
|
||||
|
||||
// Name is the function name to bind in the template
|
||||
Name string
|
||||
|
||||
// Description provides help for the function
|
||||
Description []string `json:",omitempty"`
|
||||
|
||||
// Func is the reference to the actual function
|
||||
Func interface{} `json:"-"`
|
||||
|
||||
// Function is the signature of the function
|
||||
Function string
|
||||
|
||||
// Usage shows how to use it
|
||||
Usage string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// FunctionExporter is implemented by any plugins wishing to show help on the function it exports.
|
||||
type FunctionExporter interface {
|
||||
// Funcs returns a list of special template functions of the form func(template.Context, arg1, arg2) interface{}
|
||||
Funcs() []Function
|
||||
}
|
||||
|
||||
// Context is a marker interface for a user-defined struct that is passed into the template engine (as context)
|
||||
// and accessible in the exported template functions. Template functions can have the signature
|
||||
// func(template.Context, arg1, arg2 ...) (string, error) and when functions like this are registered, the template
|
||||
// engine will dynamically create and export a function of the form func(arg1, arg2...) (string, error) where
|
||||
// the context instance becomes an out-of-band struct that can be mutated by functions. This in essence allows
|
||||
// structured data as output of the template, in addition to a string from evaluating the template.
|
||||
type Context interface {
|
||||
// Funcs returns a list of special template functions of the form func(template.Context, arg1, arg2) interface{}
|
||||
Funcs() []Function
|
||||
}
|
||||
|
||||
// Options contains parameters for customizing the behavior of the engine
|
||||
type Options struct {
|
||||
|
||||
// SocketDir is the directory for locating the socket file for
|
||||
// a template URL of the form unix://socket_file/path/to/resource
|
||||
SocketDir string
|
||||
}
|
||||
|
||||
type defaultValue struct {
|
||||
Name string
|
||||
Value interface{}
|
||||
Doc string
|
||||
}
|
||||
|
||||
// Template is the templating engine
|
||||
type Template struct {
|
||||
options Options
|
||||
|
||||
url string
|
||||
body []byte
|
||||
parsed *template.Template
|
||||
functions []func() []Function
|
||||
funcs map[string]interface{}
|
||||
globals map[string]interface{}
|
||||
defaults map[string]defaultValue
|
||||
context interface{}
|
||||
|
||||
registered []Function
|
||||
lock sync.Mutex
|
||||
|
||||
parent *Template
|
||||
}
|
||||
|
||||
// Void is used in the template functions return value type to indicate a void.
|
||||
// Golang template does not allow functions with no return types to be bound.
|
||||
type Void string
|
||||
|
||||
const voidValue Void = ""
|
||||
|
||||
// NewTemplate fetches the content at the url and returns a template. If the string begins
|
||||
// with str:// as scheme, then the rest of the string is interpreted as the body of the template.
|
||||
func NewTemplate(s string, opt Options) (*Template, error) {
|
||||
var buff []byte
|
||||
contextURL := s
|
||||
// Special case of specifying the entire template as a string; otherwise treat as url
|
||||
if strings.Index(s, "str://") == 0 {
|
||||
buff = []byte(strings.Replace(s, "str://", "", 1))
|
||||
contextURL = defaultContextURL()
|
||||
} else {
|
||||
b, err := Fetch(s, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buff = b
|
||||
}
|
||||
return NewTemplateFromBytes(buff, contextURL, opt)
|
||||
}
|
||||
|
||||
// NewTemplateFromBytes builds the template from buffer with a contextURL which is used to deduce absolute
|
||||
// path of any 'included' templates e.g. {{ include "./another.tpl" . }}
|
||||
func NewTemplateFromBytes(buff []byte, contextURL string, opt Options) (*Template, error) {
|
||||
if contextURL == "" {
|
||||
log.Warningln("Context is not known. Included templates may not work properly.")
|
||||
}
|
||||
|
||||
return &Template{
|
||||
options: opt,
|
||||
url: contextURL,
|
||||
body: buff,
|
||||
funcs: map[string]interface{}{},
|
||||
globals: map[string]interface{}{},
|
||||
defaults: map[string]defaultValue{},
|
||||
functions: []func() []Function{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetOptions sets the runtime flags for the engine
|
||||
func (t *Template) SetOptions(opt Options) *Template {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
t.options = opt
|
||||
return t
|
||||
}
|
||||
|
||||
// WithFunctions allows client code to extend the template by adding its own functions.
|
||||
func (t *Template) WithFunctions(functions func() []Function) *Template {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
t.functions = append(t.functions, functions)
|
||||
return t
|
||||
}
|
||||
|
||||
// AddFunc adds a new function to support in template
|
||||
func (t *Template) AddFunc(name string, f interface{}) *Template {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
t.funcs[name] = f
|
||||
return t
|
||||
}
|
||||
|
||||
// Ref returns the value keyed by name in the context of this template. See 'ref' template function.
|
||||
func (t *Template) Ref(name string) interface{} {
|
||||
if found, has := t.globals[name]; has {
|
||||
return found
|
||||
} else if v, has := t.defaults[name]; has {
|
||||
return v.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dot returns the '.' in this template.
|
||||
func (t *Template) Dot() interface{} {
|
||||
return t.context
|
||||
}
|
||||
|
||||
func (t *Template) forkFrom(parent *Template) (dotCopy interface{}, err error) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
// copy the globals in the parent scope into the child
|
||||
for k, v := range parent.globals {
|
||||
t.globals[k] = v
|
||||
}
|
||||
// copy the defaults in the parent scope into the child
|
||||
for k, v := range parent.defaults {
|
||||
t.defaults[k] = v
|
||||
}
|
||||
// inherit the functions defined for this template
|
||||
for k, v := range parent.funcs {
|
||||
t.AddFunc(k, v)
|
||||
}
|
||||
// inherit other functions
|
||||
for _, ff := range parent.functions {
|
||||
t.functions = append(t.functions, ff)
|
||||
}
|
||||
if parent.context != nil {
|
||||
return DeepCopyObject(parent.context)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Global sets the a key, value in the context of this template. It is visible to all the 'included'
|
||||
// and 'sourced' templates by the calling template.
|
||||
func (t *Template) Global(name string, value interface{}) *Template {
|
||||
for here := t; here != nil; here = here.parent {
|
||||
here.updateGlobal(name, value)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *Template) updateGlobal(name string, value interface{}) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
t.globals[name] = value
|
||||
}
|
||||
|
||||
// Def is equivalent to a {{ def "key" value "description" }} in defining a variable with a default value.
|
||||
// The value is accessible via a {{ ref "key" }} in the template.
|
||||
func (t *Template) Def(name string, value interface{}, doc string) *Template {
|
||||
for here := t; here != nil; here = here.parent {
|
||||
here.updateDef(name, value, doc)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *Template) updateDef(name string, val interface{}, doc ...string) *Template {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
t.defaults[name] = defaultValue{
|
||||
Name: name,
|
||||
Value: val,
|
||||
Doc: strings.Join(doc, " "),
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Validate parses the template and checks for validity.
|
||||
func (t *Template) Validate() (*Template, error) {
|
||||
t.lock.Lock()
|
||||
t.parsed = nil
|
||||
t.lock.Unlock()
|
||||
return t, t.build(nil)
|
||||
}
|
||||
|
||||
// Funcs returns a list of registered functions used by the template when it rendered the view.
|
||||
func (t *Template) Funcs() []Function {
|
||||
return t.registered
|
||||
}
|
||||
|
||||
func (t *Template) build(context Context) error {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
if t.parsed != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
registered := []Function{}
|
||||
fm := map[string]interface{}{}
|
||||
|
||||
for k, v := range sprig.TxtFuncMap() {
|
||||
fm[k] = v
|
||||
}
|
||||
|
||||
for k, v := range t.funcs {
|
||||
if tf, err := makeTemplateFunc(context, v); err == nil {
|
||||
fm[k] = tf
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// the default functions cannot be overriden
|
||||
for _, f := range t.DefaultFuncs() {
|
||||
tf, err := makeTemplateFunc(context, f.Func)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fm[f.Name] = tf
|
||||
registered = append(registered, f)
|
||||
}
|
||||
|
||||
// If there are any function sources that was set via WithFunctions()
|
||||
for _, exp := range t.functions {
|
||||
for _, f := range exp() {
|
||||
tf, err := makeTemplateFunc(context, f.Func)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fm[f.Name] = tf
|
||||
registered = append(registered, f)
|
||||
}
|
||||
}
|
||||
|
||||
// If the context implements the FunctionExporter interface, it can add more functions
|
||||
// and potentially override existing.
|
||||
if context != nil {
|
||||
for _, f := range context.Funcs() {
|
||||
if tf, err := makeTemplateFunc(context, f.Func); err == nil {
|
||||
fm[f.Name] = tf
|
||||
registered = append(registered, f)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.registered = registered
|
||||
|
||||
parsed, err := template.New(t.url).Funcs(fm).Parse(string(t.body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.parsed = parsed
|
||||
return nil
|
||||
}
|
||||
|
||||
// Execute is a drop-in replace of the execute method of template
|
||||
func (t *Template) Execute(output io.Writer, context interface{}) error {
|
||||
if err := t.build(toContext(context)); err != nil {
|
||||
return err
|
||||
}
|
||||
t.context = context
|
||||
return t.parsed.Execute(output, context)
|
||||
}
|
||||
|
||||
// returns as Context if input implements the interface; otherwise nil
|
||||
func toContext(in interface{}) Context {
|
||||
var context Context
|
||||
if in != nil {
|
||||
if s, is := in.(Context); is {
|
||||
context = s
|
||||
}
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
// Render renders the template given the context
|
||||
func (t *Template) Render(context interface{}) (string, error) {
|
||||
if err := t.build(toContext(context)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
var buff bytes.Buffer
|
||||
err := t.Execute(&buff, context)
|
||||
return buff.String(), err
|
||||
}
|
||||
|
||||
// converts a function of f(Context, ags...) to a regular template function
|
||||
func makeTemplateFunc(ctx Context, f interface{}) (interface{}, error) {
|
||||
|
||||
contextType := reflect.TypeOf((*Context)(nil)).Elem()
|
||||
|
||||
ff := reflect.Indirect(reflect.ValueOf(f))
|
||||
// first we check to see if f has the special signature where the first
|
||||
// parameter is the context parameter...
|
||||
if ff.Kind() != reflect.Func {
|
||||
return nil, fmt.Errorf("not a function:%v", f)
|
||||
}
|
||||
|
||||
if ff.Type().NumIn() > 0 && ff.Type().In(0).AssignableTo(contextType) {
|
||||
|
||||
in := make([]reflect.Type, ff.Type().NumIn()-1) // exclude the context param
|
||||
out := make([]reflect.Type, ff.Type().NumOut())
|
||||
|
||||
for i := 1; i < ff.Type().NumIn(); i++ {
|
||||
in[i-1] = ff.Type().In(i)
|
||||
}
|
||||
variadic := false
|
||||
if len(in) > 0 {
|
||||
variadic = in[len(in)-1].Kind() == reflect.Slice
|
||||
}
|
||||
for i := 0; i < ff.Type().NumOut(); i++ {
|
||||
out[i] = ff.Type().Out(i)
|
||||
}
|
||||
funcType := reflect.FuncOf(in, out, variadic)
|
||||
funcImpl := func(in []reflect.Value) []reflect.Value {
|
||||
if !variadic {
|
||||
return ff.Call(append([]reflect.Value{reflect.ValueOf(ctx)}, in...))
|
||||
}
|
||||
|
||||
variadicParam := in[len(in)-1]
|
||||
last := make([]reflect.Value, variadicParam.Len())
|
||||
for i := 0; i < variadicParam.Len(); i++ {
|
||||
last[i] = variadicParam.Index(i)
|
||||
}
|
||||
return ff.Call(append(append([]reflect.Value{reflect.ValueOf(ctx)}, in[0:len(in)-1]...), last...))
|
||||
}
|
||||
|
||||
newFunc := reflect.MakeFunc(funcType, funcImpl)
|
||||
return newFunc.Interface(), nil
|
||||
}
|
||||
return ff.Interface(), nil
|
||||
}
|
||||
28
vendor/github.com/docker/infrakit/pkg/template/util.go
generated
vendored
Normal file
28
vendor/github.com/docker/infrakit/pkg/template/util.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// returns a url string of the base and a relative path.
|
||||
// e.g. http://host/foo/bar/baz, ./boo.tpl gives http://host/foo/bar/boo.tpl
|
||||
func getURL(root, rel string) (string, error) {
|
||||
|
||||
// handle the case when rel is actually a full url
|
||||
if strings.Index(rel, "://") > 0 {
|
||||
u, err := url.Parse(rel)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
u, err := url.Parse(root)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
u.Path = filepath.Clean(filepath.Join(filepath.Dir(u.Path), rel))
|
||||
return u.String(), nil
|
||||
}
|
||||
92
vendor/github.com/docker/infrakit/pkg/types/any.go
generated
vendored
Normal file
92
vendor/github.com/docker/infrakit/pkg/types/any.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Any is the raw configuration for the plugin
|
||||
type Any json.RawMessage
|
||||
|
||||
// AnyString returns an Any from a string that represents the marshaled/encoded data
|
||||
func AnyString(s string) *Any {
|
||||
return AnyBytes([]byte(s))
|
||||
}
|
||||
|
||||
// AnyBytes returns an Any from the encoded message bytes
|
||||
func AnyBytes(data []byte) *Any {
|
||||
any := &Any{}
|
||||
*any = data
|
||||
return any
|
||||
}
|
||||
|
||||
// AnyCopy makes a copy of the data in the given ptr.
|
||||
func AnyCopy(any *Any) *Any {
|
||||
if any == nil {
|
||||
return &Any{}
|
||||
}
|
||||
return AnyBytes(any.Bytes())
|
||||
}
|
||||
|
||||
// AnyValue returns an Any from a value by marshaling / encoding the input
|
||||
func AnyValue(v interface{}) (*Any, error) {
|
||||
if v == nil {
|
||||
return nil, nil // So that any omitempty will see an empty/zero value
|
||||
}
|
||||
any := &Any{}
|
||||
err := any.marshal(v)
|
||||
return any, err
|
||||
}
|
||||
|
||||
// AnyValueMust returns an Any from a value by marshaling / encoding the input. It panics if there's error.
|
||||
func AnyValueMust(v interface{}) *Any {
|
||||
any, err := AnyValue(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return any
|
||||
}
|
||||
|
||||
// Decode decodes the any into the input typed struct
|
||||
func (c *Any) Decode(typed interface{}) error {
|
||||
if c == nil || len([]byte(*c)) == 0 {
|
||||
return nil // no effect on typed
|
||||
}
|
||||
return json.Unmarshal([]byte(*c), typed)
|
||||
}
|
||||
|
||||
// marshal populates this raw message with a decoded form of the input struct.
|
||||
func (c *Any) marshal(typed interface{}) error {
|
||||
buff, err := json.MarshalIndent(typed, "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*c = Any(json.RawMessage(buff))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bytes returns the encoded bytes
|
||||
func (c *Any) Bytes() []byte {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return []byte(*c)
|
||||
}
|
||||
|
||||
// String returns the string representation.
|
||||
func (c *Any) String() string {
|
||||
return string([]byte(*c))
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json Marshaler interface
|
||||
func (c *Any) MarshalJSON() ([]byte, error) {
|
||||
if c == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return []byte(*c), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json Unmarshaler interface
|
||||
func (c *Any) UnmarshalJSON(data []byte) error {
|
||||
*c = Any(json.RawMessage(data))
|
||||
return nil
|
||||
}
|
||||
124
vendor/github.com/docker/infrakit/pkg/types/link.go
generated
vendored
Normal file
124
vendor/github.com/docker/infrakit/pkg/types/link.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(int64(time.Now().Nanosecond()))
|
||||
}
|
||||
|
||||
// Link is a struct that represents an association between an infrakit managed resource
|
||||
// and an entity in some other system. The mechanism of linkage is via labels or tags
|
||||
// on both sides.
|
||||
type Link struct {
|
||||
value string
|
||||
context string
|
||||
}
|
||||
|
||||
// NewLink creates a link
|
||||
func NewLink() *Link {
|
||||
return &Link{
|
||||
value: randomAlphaNumericString(16),
|
||||
}
|
||||
}
|
||||
|
||||
// NewLinkFromMap constructs a link from data in the map
|
||||
func NewLinkFromMap(m map[string]string) *Link {
|
||||
l := &Link{}
|
||||
if v, has := m["infrakit-link"]; has {
|
||||
l.value = v
|
||||
}
|
||||
|
||||
if v, has := m["infrakit-link-context"]; has {
|
||||
l.context = v
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// Valid returns true if the link value is set
|
||||
func (l Link) Valid() bool {
|
||||
return l.value != ""
|
||||
}
|
||||
|
||||
// Value returns the value of the link
|
||||
func (l Link) Value() string {
|
||||
return l.value
|
||||
}
|
||||
|
||||
// Label returns the label to look for the link
|
||||
func (l Link) Label() string {
|
||||
return "infrakit-link"
|
||||
}
|
||||
|
||||
// Context returns the context of the link
|
||||
func (l Link) Context() string {
|
||||
return l.context
|
||||
}
|
||||
|
||||
// WithContext sets a context for this link
|
||||
func (l *Link) WithContext(s string) *Link {
|
||||
l.context = s
|
||||
return l
|
||||
}
|
||||
|
||||
// KVPairs returns the link representation as a slice of Key=Value pairs
|
||||
func (l *Link) KVPairs() []string {
|
||||
out := []string{}
|
||||
for k, v := range l.Map() {
|
||||
out = append(out, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Map returns a representation that is easily converted to JSON or YAML
|
||||
func (l *Link) Map() map[string]string {
|
||||
return map[string]string{
|
||||
"infrakit-link": l.value,
|
||||
"infrakit-link-context": l.context,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteMap writes to the target map. This will overwrite values of same key
|
||||
func (l *Link) WriteMap(target map[string]string) {
|
||||
for k, v := range l.Map() {
|
||||
target[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// InMap returns true if the link is contained in the map
|
||||
func (l *Link) InMap(m map[string]string) bool {
|
||||
c, has := m["infrakit-link-context"]
|
||||
if !has {
|
||||
return false
|
||||
}
|
||||
if c != l.context {
|
||||
return false
|
||||
}
|
||||
|
||||
v, has := m["infrakit-link"]
|
||||
if !has {
|
||||
return false
|
||||
}
|
||||
return v == l.value
|
||||
}
|
||||
|
||||
// Equal returns true if the links are the same - same value and context
|
||||
func (l *Link) Equal(other Link) bool {
|
||||
return l.value == other.value && l.context == other.context
|
||||
}
|
||||
|
||||
// randomAlphaNumericString generates a non-secure random alpha-numeric string of a given length.
|
||||
func randomAlphaNumericString(length int) string {
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = letters[rand.Intn(len(letters))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
200
vendor/github.com/docker/infrakit/pkg/types/path.go
generated
vendored
Normal file
200
vendor/github.com/docker/infrakit/pkg/types/path.go
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// NullPath means no path
|
||||
NullPath = Path([]string{})
|
||||
)
|
||||
|
||||
// Path is used to identify a particle of metadata. The path can be strings separated by / as in a URL.
|
||||
type Path []string
|
||||
|
||||
// PathFromString returns the path components of a / separated path
|
||||
func PathFromString(path string) Path {
|
||||
return Path(strings.Split(path, "/")).Clean()
|
||||
}
|
||||
|
||||
// PathFrom return a single path of the given components
|
||||
func PathFrom(a string, b ...string) Path {
|
||||
p := Path(append([]string{a}, b...))
|
||||
return p.Clean()
|
||||
}
|
||||
|
||||
// PathFromStrings returns the path from a list of strings
|
||||
func PathFromStrings(a string, b ...string) []Path {
|
||||
list := []Path{PathFromString(a)}
|
||||
for _, p := range b {
|
||||
list = append(list, PathFromString(p))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// String returns the string representation of path
|
||||
func (p Path) String() string {
|
||||
s := strings.Join([]string(p), "/")
|
||||
if len(s) == 0 {
|
||||
return "."
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Valid returns true if is a valid path
|
||||
func (p Path) Valid() bool {
|
||||
return p.Len() > 0
|
||||
}
|
||||
|
||||
// Dot returns true if this is a .
|
||||
func (p Path) Dot() bool {
|
||||
return len(p) == 1 && p[0] == "."
|
||||
}
|
||||
|
||||
// Clean scrubs the path to remove any empty string or . or .. and collapse the path into a concise form.
|
||||
// It's similar to path.Clean in the standard lib.
|
||||
func (p Path) Clean() Path {
|
||||
this := []string(p)
|
||||
copy := []string{}
|
||||
for _, v := range this {
|
||||
switch v {
|
||||
case "", ".":
|
||||
case "..":
|
||||
if len(copy) == 0 {
|
||||
copy = append(copy, "..")
|
||||
} else {
|
||||
copy = copy[0 : len(copy)-1]
|
||||
if len(copy) == 0 {
|
||||
return NullPath
|
||||
}
|
||||
}
|
||||
default:
|
||||
copy = append(copy, v)
|
||||
}
|
||||
}
|
||||
if len(copy) == 0 {
|
||||
copy = []string{"."}
|
||||
} else if this[len(this)-1] == "" || this[len(this)-1] == "." {
|
||||
copy = append(copy, "")
|
||||
}
|
||||
|
||||
return Path(copy)
|
||||
}
|
||||
|
||||
// Len returns the length of the path
|
||||
func (p Path) Len() int {
|
||||
return len([]string(p))
|
||||
}
|
||||
|
||||
// Index returns the ith component in the path
|
||||
func (p Path) Index(i int) *string {
|
||||
if p.Len() <= i {
|
||||
return nil
|
||||
}
|
||||
copy := []string(p)[i]
|
||||
return ©
|
||||
}
|
||||
|
||||
// Shift returns a new path that's shifted i positions to the left -- ith child of the head at index=0
|
||||
func (p Path) Shift(i int) Path {
|
||||
len := p.Len() - i
|
||||
if len <= 0 {
|
||||
return Path([]string{})
|
||||
}
|
||||
new := make([]string, len)
|
||||
copy(new, []string(p)[i:])
|
||||
return Path(new)
|
||||
}
|
||||
|
||||
// Dir returns the 'dir' of the path
|
||||
func (p Path) Dir() Path {
|
||||
pp := p.Clean()
|
||||
if len(pp) > 1 {
|
||||
return p[0 : len(pp)-1]
|
||||
}
|
||||
return Path([]string{"."})
|
||||
}
|
||||
|
||||
// Base returns the base of the path
|
||||
func (p Path) Base() string {
|
||||
pp := p.Clean()
|
||||
return pp[len(pp)-1]
|
||||
}
|
||||
|
||||
// JoinString joins the input as a child of this path
|
||||
func (p Path) JoinString(child string) Path {
|
||||
return p.Join(Path([]string{child}))
|
||||
}
|
||||
|
||||
// Join joins the child to the parent
|
||||
func (p Path) Join(child Path) Path {
|
||||
pp := p.Clean()
|
||||
this := []string(pp)
|
||||
if this[len(this)-1] == "" {
|
||||
pp = Path(this[:len(this)-1])
|
||||
}
|
||||
return Path(append(pp, []string(child)...))
|
||||
}
|
||||
|
||||
// Rel returns a new path that is a child of the input from this path.
|
||||
// e.g. For a path a/b/c/d Rel(a/b/) returns c/d. NullPath is returned if
|
||||
// the two are not relative to one another.
|
||||
func (p Path) Rel(path Path) Path {
|
||||
if path.Equal(PathFromString(".")) {
|
||||
return p
|
||||
}
|
||||
|
||||
this := []string(p.Clean())
|
||||
parent := []string(path.Clean())
|
||||
if parent[len(parent)-1] == "" {
|
||||
parent = parent[:len(parent)-1]
|
||||
}
|
||||
if len(this) < len(parent) {
|
||||
return NullPath
|
||||
}
|
||||
for i := 0; i < len(parent); i++ {
|
||||
if parent[i] != this[i] {
|
||||
return NullPath
|
||||
}
|
||||
}
|
||||
return Path(this[len(parent):])
|
||||
}
|
||||
|
||||
// Equal returns true if the path is lexicographically equal to the other
|
||||
func (p Path) Equal(other Path) bool {
|
||||
if len(p) != len(other) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(p); i++ {
|
||||
if p[i] != other[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Less return true if the path is lexicographically less than the other
|
||||
func (p Path) Less(other Path) bool {
|
||||
min := len(p)
|
||||
if len(other) < min {
|
||||
min = len(other)
|
||||
}
|
||||
for i := 0; i < min; i++ {
|
||||
if string(p[i]) != string(other[i]) {
|
||||
return string(p[i]) < string(other[i])
|
||||
}
|
||||
}
|
||||
return len(p) < len(other)
|
||||
}
|
||||
|
||||
type pathSorter []Path
|
||||
|
||||
func (p pathSorter) Len() int { return len(p) }
|
||||
func (p pathSorter) Less(i, j int) bool { return Path(p[i]).Less(Path(p[j])) }
|
||||
func (p pathSorter) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
// Sort sorts the paths
|
||||
func Sort(p []Path) {
|
||||
sort.Sort(pathSorter(p))
|
||||
}
|
||||
233
vendor/github.com/docker/infrakit/pkg/types/reflect.go
generated
vendored
Normal file
233
vendor/github.com/docker/infrakit/pkg/types/reflect.go
generated
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
indexRoot = "\\[(([+|-]*[0-9]+)|((.*)=(.*)))\\]$"
|
||||
arrayIndexExp = regexp.MustCompile("(.*)" + indexRoot)
|
||||
indexExp = regexp.MustCompile("^" + indexRoot)
|
||||
)
|
||||
|
||||
// Put sets the attribute of an object at path to the given value
|
||||
func Put(path []string, value interface{}, object map[string]interface{}) bool {
|
||||
return put(path, value, object)
|
||||
}
|
||||
|
||||
// Get returns the attribute of the object at path
|
||||
func Get(path []string, object interface{}) interface{} {
|
||||
return get(path, object)
|
||||
}
|
||||
|
||||
// GetValue returns the attribute of the object at path, as serialized blob
|
||||
func GetValue(path []string, object interface{}) (*Any, error) {
|
||||
if any, is := object.(*Any); is {
|
||||
return any, nil
|
||||
}
|
||||
return AnyValue(Get(path, object))
|
||||
}
|
||||
|
||||
// List lists the members at the path
|
||||
// If the value at the path is listable, then a slice of the children (unqualified) will be
|
||||
// returned. If the value at the path doesn't exist, a nil will be returned.
|
||||
// A value is listable if it is 1. a slice, 2. a map, 3. a struct (fields will be listed)
|
||||
// or a 4. a types.Any where the any object will be unpacked and the same rules above will be
|
||||
// applied to the decoded struct.
|
||||
func List(path []string, object interface{}) []string {
|
||||
list := []string{}
|
||||
v := get(path, object)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
val := reflect.Indirect(reflect.ValueOf(v))
|
||||
|
||||
if any, is := v.(*Any); is {
|
||||
var temp interface{}
|
||||
if err := any.Decode(&temp); err == nil {
|
||||
val = reflect.ValueOf(temp)
|
||||
}
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Slice:
|
||||
// this is a slice, so return the name as '[%d]'
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
list = append(list, fmt.Sprintf("[%d]", i))
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
for _, k := range val.MapKeys() {
|
||||
list = append(list, k.String())
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
vt := val.Type()
|
||||
for i := 0; i < vt.NumField(); i++ {
|
||||
if vt.Field(i).PkgPath == "" {
|
||||
list = append(list, vt.Field(i).Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(list)
|
||||
return list
|
||||
}
|
||||
|
||||
func put(p []string, value interface{}, store map[string]interface{}) bool {
|
||||
if len(p) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
key := p[0]
|
||||
if key == "" {
|
||||
return put(p[1:], value, store)
|
||||
}
|
||||
// check if key is an array index of the form <1>[<2>]
|
||||
matches := arrayIndexExp.FindStringSubmatch(key)
|
||||
if len(matches) > 2 && matches[1] != "" {
|
||||
key = matches[1]
|
||||
p = append([]string{key, fmt.Sprintf("[%s]", matches[2])}, p[1:]...)
|
||||
return put(p, value, store)
|
||||
}
|
||||
|
||||
s := reflect.Indirect(reflect.ValueOf(store))
|
||||
switch s.Kind() {
|
||||
case reflect.Slice:
|
||||
return false // not supported
|
||||
|
||||
case reflect.Map:
|
||||
if reflect.TypeOf(p[0]).AssignableTo(s.Type().Key()) {
|
||||
m := s.MapIndex(reflect.ValueOf(p[0]))
|
||||
if !m.IsValid() {
|
||||
m = reflect.ValueOf(map[string]interface{}{})
|
||||
s.SetMapIndex(reflect.ValueOf(p[0]), m)
|
||||
}
|
||||
if len(p) > 1 {
|
||||
return put(p[1:], value, m.Interface().(map[string]interface{}))
|
||||
}
|
||||
s.SetMapIndex(reflect.ValueOf(p[0]), reflect.ValueOf(value))
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func get(path []string, object interface{}) (value interface{}) {
|
||||
if f, is := object.(func() interface{}); is {
|
||||
object = f()
|
||||
}
|
||||
|
||||
if len(path) == 0 {
|
||||
return object
|
||||
}
|
||||
|
||||
if any, is := object.(*Any); is {
|
||||
var temp interface{}
|
||||
if err := any.Decode(&temp); err == nil {
|
||||
return get(path, temp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
key := path[0]
|
||||
|
||||
switch key {
|
||||
case ".":
|
||||
return object
|
||||
case "":
|
||||
return get(path[1:], object)
|
||||
}
|
||||
|
||||
// check if key is an array index of the form <1>[<2>]
|
||||
matches := arrayIndexExp.FindStringSubmatch(key)
|
||||
if len(matches) > 2 && matches[1] != "" {
|
||||
key = matches[1]
|
||||
path = append([]string{key, fmt.Sprintf("[%s]", matches[2])}, path[1:]...)
|
||||
return get(path, object)
|
||||
}
|
||||
|
||||
v := reflect.Indirect(reflect.ValueOf(object))
|
||||
switch v.Kind() {
|
||||
case reflect.Slice:
|
||||
i := 0
|
||||
matches = indexExp.FindStringSubmatch(key)
|
||||
if len(matches) > 0 {
|
||||
if matches[2] != "" {
|
||||
// numeric index
|
||||
if index, err := strconv.Atoi(matches[1]); err == nil {
|
||||
switch {
|
||||
case index >= 0 && v.Len() > index:
|
||||
i = index
|
||||
case index < 0 && v.Len() > -index: // negative index like python
|
||||
i = v.Len() + index
|
||||
}
|
||||
}
|
||||
return get(path[1:], v.Index(i).Interface())
|
||||
|
||||
} else if matches[3] != "" {
|
||||
// equality search index for 'field=check'
|
||||
lhs := matches[4] // supports another select expression for extracting deeply from the struct
|
||||
rhs := matches[5]
|
||||
// loop through the array looking for field that matches the check value
|
||||
for j := 0; j < v.Len(); j++ {
|
||||
if el := get(tokenize(lhs), v.Index(j).Interface()); el != nil {
|
||||
if fmt.Sprintf("%v", el) == rhs {
|
||||
return get(path[1:], v.Index(j).Interface())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
value := v.MapIndex(reflect.ValueOf(key))
|
||||
if value.IsValid() {
|
||||
return get(path[1:], value.Interface())
|
||||
}
|
||||
case reflect.Struct:
|
||||
fv := v.FieldByName(key)
|
||||
if !fv.IsValid() {
|
||||
return nil
|
||||
}
|
||||
if !fv.CanInterface() {
|
||||
return nil
|
||||
}
|
||||
return get(path[1:], fv.Interface())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// With quoting to support azure rm type names: e.g. Microsoft.Network/virtualNetworks
|
||||
// This will split a sting like /Resources/'Microsoft.Network/virtualNetworks'/managerSubnet/Name" into
|
||||
// [ , Resources, Microsoft.Network/virtualNetworks, managerSubnet, Name]
|
||||
func tokenize(s string) []string {
|
||||
if len(s) == 0 {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
a := []string{}
|
||||
start := 0
|
||||
quoted := false
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
case '/':
|
||||
if !quoted {
|
||||
a = append(a, strings.Replace(s[start:i], "'", "", -1))
|
||||
start = i + 1
|
||||
}
|
||||
case '\'':
|
||||
quoted = !quoted
|
||||
}
|
||||
}
|
||||
if start < len(s)-1 {
|
||||
a = append(a, strings.Replace(s[start:], "'", "", -1))
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
Reference in New Issue
Block a user