mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Remove cli dir
This commit is contained in:
6
cli/.gitignore
vendored
6
cli/.gitignore
vendored
@@ -1,6 +0,0 @@
|
||||
/fn.exe
|
||||
/fn_linux
|
||||
/fn_mac
|
||||
/fn
|
||||
/fn_alpine
|
||||
tmp/
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
|
||||
Run `make install` to build and install to local machine for easy testing.
|
||||
@@ -1,13 +0,0 @@
|
||||
FROM alpine
|
||||
|
||||
|
||||
RUN apk --update upgrade && \
|
||||
apk add curl ca-certificates && \
|
||||
update-ca-certificates && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
COPY entrypoint.sh /
|
||||
COPY fn /
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
30
cli/Makefile
30
cli/Makefile
@@ -1,30 +0,0 @@
|
||||
all: vendor build
|
||||
./fn
|
||||
|
||||
build:
|
||||
go build -o fn
|
||||
|
||||
install:
|
||||
go build -o ${GOPATH}/bin/fn
|
||||
|
||||
docker: vendor
|
||||
GOOS=linux go build -o fn
|
||||
docker build -t fnproject/fn .
|
||||
docker push fnproject/fn
|
||||
|
||||
dep:
|
||||
glide install -v
|
||||
|
||||
dep-up:
|
||||
glide up -v
|
||||
|
||||
test:
|
||||
./test.sh
|
||||
|
||||
release:
|
||||
GOOS=linux go build -o fn_linux
|
||||
GOOS=darwin go build -o fn_mac
|
||||
GOOS=windows go build -o fn.exe
|
||||
docker run --rm -v ${PWD}:/go/src/github.com/fnproject/fn/cli -w /go/src/github.com/fnproject/fn/cli funcy/go:dev go build -o fn_alpine
|
||||
|
||||
.PHONY: install
|
||||
270
cli/README.md
270
cli/README.md
@@ -1,270 +0,0 @@
|
||||
# Oracle Functions CLI
|
||||
|
||||
## Creating Functions
|
||||
|
||||
### init
|
||||
|
||||
Init will help you create a [function file](../docs/function-file.md) (func.yaml) in the current directory.
|
||||
|
||||
To make things simple, we try to use convention over configuration, so `init` will look for a file named `func.{language-extension}`. For example,
|
||||
if you are using Node, put the code that you want to execute in the file `func.js`. If you are using Python, use `func.py`. Ruby, use `func.rb`. Go, `func.go`. Etc.
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
fn init [<FUNCTION_NAME>]
|
||||
```
|
||||
|
||||
If you want to override the convention with configuration, you can do that as well using:
|
||||
|
||||
```sh
|
||||
fn init [--runtime node] [--entrypoint "node hello.js"] [<FUNCTION_NAME>]
|
||||
```
|
||||
|
||||
Or, if you want full control, just make a Dockerfile. If `init` finds a Dockerfile, it will use that instead of runtime and entrypoint.
|
||||
|
||||
### Bump, Build, Run, Push
|
||||
|
||||
`fn` provides a few commands you'll use while creating and updating your functions: `bump`, `build`, `run` and `push`.
|
||||
|
||||
Bump will bump the version number in your func.yaml file. Versions must be in [semver](http://semver.org/) format.
|
||||
|
||||
```sh
|
||||
fn bump
|
||||
```
|
||||
|
||||
Build will build the image for your function, creating a Docker image tagged with the version number from func.yaml.
|
||||
|
||||
```sh
|
||||
fn build
|
||||
```
|
||||
|
||||
Run will help you test your function. Functions read input from STDIN, so you can pipe the payload into the function like this:
|
||||
|
||||
```sh
|
||||
cat `payload.json` | fn run
|
||||
```
|
||||
|
||||
Push will push the function image to Docker Hub.
|
||||
|
||||
```sh
|
||||
fn push
|
||||
```
|
||||
|
||||
## Using the API
|
||||
|
||||
You can operate Oracle Functions from the command line.
|
||||
|
||||
```sh
|
||||
$ fn apps list # list apps
|
||||
myapp
|
||||
|
||||
$ fn apps create otherapp # create new app
|
||||
otherapp created
|
||||
|
||||
$ fn apps inspect otherapp config # show app-specific configuration
|
||||
{ ... }
|
||||
|
||||
$ fn apps
|
||||
myapp
|
||||
otherapp
|
||||
|
||||
$ fn routes list myapp # list routes of an app
|
||||
path image
|
||||
/hello fnproject/hello
|
||||
|
||||
$ fn routes create otherapp /hello fnproject/hello # create route
|
||||
/hello created with fnproject/hello
|
||||
|
||||
$ fn routes delete otherapp hello # delete route
|
||||
/hello deleted
|
||||
|
||||
$ fn routes headers set otherapp hello header-name value # add HTTP header to response
|
||||
otherapp /hello headers updated header-name with value
|
||||
|
||||
|
||||
$ fn calls list myapp /hello # lists all available calls for /hello route from myapp
|
||||
ID: 45bd486b-6eec-548a-bc1a-94d59deef4ac
|
||||
App: myapp
|
||||
Route: /hello
|
||||
Created At: 2017-06-02T15:23:53.263+03:00
|
||||
Started At: 2017-06-02T15:23:53.263+03:00
|
||||
Completed At: 2017-06-02T15:23:53.532+03:00
|
||||
Status: success
|
||||
|
||||
$ fn calls get 45bd486b-6eec-548a-bc1a-94d59deef4ac # gets specific calls by ID
|
||||
ID: 45bd486b-6eec-548a-bc1a-94d59deef4ac
|
||||
App: myapp
|
||||
Route: /hello
|
||||
Created At: 2017-06-02T15:23:53.263+03:00
|
||||
Started At: 2017-06-02T15:23:53.263+03:00
|
||||
Completed At: 2017-06-02T15:23:53.532+03:00
|
||||
Status: success
|
||||
|
||||
|
||||
$ fn version # shows version both of client and server
|
||||
Client version: 0.3.7
|
||||
Server version 0.3.7
|
||||
```
|
||||
|
||||
## Application level configuration
|
||||
|
||||
When creating an application, you can configure it to tweak its behavior and its
|
||||
routes' with an appropriate flag, `config`.
|
||||
|
||||
Thus a more complete example of an application creation will look like:
|
||||
```sh
|
||||
fn apps create --config DB_URL=http://example.org/ otherapp
|
||||
```
|
||||
|
||||
`--config` is a map of values passed to the route runtime in the form of
|
||||
environment variables.
|
||||
|
||||
Repeated calls to `fn apps create` will trigger an update of the given
|
||||
route, thus you will be able to change any of these attributes later in time
|
||||
if necessary.
|
||||
|
||||
## Route level configuration
|
||||
|
||||
When creating a route, you can configure it to tweak its behavior, the possible
|
||||
choices are: `memory`, `type` and `config`.
|
||||
|
||||
Thus a more complete example of route creation will look like:
|
||||
```sh
|
||||
fn routes create --memory 256 --type async --config DB_URL=http://example.org/ otherapp /hello fnproject/hello
|
||||
```
|
||||
|
||||
You can also update existent routes configurations using the command `fn routes update`
|
||||
|
||||
For example:
|
||||
|
||||
```sh
|
||||
fn routes update --memory 64 --type sync --image fnproject/hello
|
||||
```
|
||||
|
||||
To know exactly what configurations you can update just use the command
|
||||
|
||||
```
|
||||
fn routes update --help
|
||||
```
|
||||
|
||||
To understand how each configuration affect your function checkout the [Definitions](/docs/definitions.md#Routes) document.
|
||||
|
||||
## Changing target host
|
||||
|
||||
`fn` is configured by default to talk http://localhost:8080.
|
||||
You may reconfigure it to talk to a remote installation by updating a local
|
||||
environment variable (`$API_URL`):
|
||||
```sh
|
||||
$ export API_URL="http://myfunctions.example.org/"
|
||||
$ fn ...
|
||||
```
|
||||
|
||||
## Bulk deploy
|
||||
|
||||
Also there is the `deploy` command that is going to scan all local directory for
|
||||
functions, rebuild them and push them to Docker Hub and update them in
|
||||
Oracle Functions. It will use the `route` entry in the existing function file to
|
||||
see the update in the daemon.
|
||||
|
||||
|
||||
```sh
|
||||
$ fn deploy APP
|
||||
```
|
||||
|
||||
`fn deploy` expects that each directory to contain a file `func.yaml`
|
||||
which instructs `fn` on how to act with that particular update.
|
||||
|
||||
## Testing functions
|
||||
|
||||
If you added `tests` to the `func.yaml` file, you can have them tested using
|
||||
`fn test`.
|
||||
|
||||
```sh
|
||||
$ fn test
|
||||
```
|
||||
|
||||
During local development cycles, you can easily force a build before test:
|
||||
```sh
|
||||
$ fn test -b
|
||||
```
|
||||
|
||||
When preparing to deploy you application, remember adding `path` to `func.yaml`,
|
||||
it will simplify both the creation of the route, and the execution of remote
|
||||
tests:
|
||||
```yaml
|
||||
name: me/myapp
|
||||
version: 1.0.0
|
||||
path: /myfunc
|
||||
```
|
||||
|
||||
Once you application is done and deployed, you can run tests remotely:
|
||||
```
|
||||
# test the function locally first
|
||||
$ fn test -b
|
||||
|
||||
# push it to Docker Hub and Oracle Functions
|
||||
$ fn push
|
||||
$ fn routes create myapp
|
||||
|
||||
# test it remotely
|
||||
$ fn test --remote myapp
|
||||
```
|
||||
|
||||
## Other examples of usage
|
||||
|
||||
### Creating a new function from source
|
||||
```
|
||||
fn init fnproject/hello --runtime ruby
|
||||
fn deploy myapp /hello
|
||||
```
|
||||
|
||||
### Updating function
|
||||
```
|
||||
fn deploy myapp (discover route path if available in func.yaml)
|
||||
```
|
||||
|
||||
### Testing function locally
|
||||
```
|
||||
fn run fnproject/hello
|
||||
```
|
||||
|
||||
### Testing route
|
||||
```
|
||||
fn call myapp /hello
|
||||
```
|
||||
|
||||
### App management
|
||||
```
|
||||
fn apps create myapp
|
||||
fn apps update myapp --headers "content-type=application/json"
|
||||
fn apps config set log_level info
|
||||
fn apps inspect myapp
|
||||
fn apps delete myapp
|
||||
```
|
||||
|
||||
### Route management
|
||||
```
|
||||
fn routes create myapp /hello fnproject/hello
|
||||
# routes update will also update any changes in the func.yaml file too.
|
||||
fn routes update myapp /hello --timeout 30 --type async
|
||||
fn routes config set myapp /hello log_level info
|
||||
fn routes inspect myapp /hello
|
||||
fn routes delete myapp /hello
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Ensure you have Go configured and installed in your environment. Once it is
|
||||
done, run:
|
||||
|
||||
```sh
|
||||
$ make
|
||||
```
|
||||
|
||||
It will build fn compatible with your local environment. You can test this
|
||||
CLI, right away with:
|
||||
|
||||
```sh
|
||||
$ ./fn
|
||||
```
|
||||
311
cli/apps.go
311
cli/apps.go
@@ -1,311 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
client "github.com/fnproject/fn/cli/client"
|
||||
fnclient "github.com/funcy/functions_go/client"
|
||||
apiapps "github.com/funcy/functions_go/client/apps"
|
||||
"github.com/funcy/functions_go/models"
|
||||
"github.com/jmoiron/jsonq"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type appsCmd struct {
|
||||
client *fnclient.Functions
|
||||
}
|
||||
|
||||
func apps() cli.Command {
|
||||
a := appsCmd{client: client.APIClient()}
|
||||
|
||||
return cli.Command{
|
||||
Name: "apps",
|
||||
Usage: "manage applications",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "create",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "create a new app",
|
||||
ArgsUsage: "<app>",
|
||||
Action: a.create,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringSliceFlag{
|
||||
Name: "config",
|
||||
Usage: "application configuration",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "inspect",
|
||||
Aliases: []string{"i"},
|
||||
Usage: "retrieve one or all apps properties",
|
||||
ArgsUsage: "<app> [property.[key]]",
|
||||
Action: a.inspect,
|
||||
},
|
||||
{
|
||||
Name: "update",
|
||||
Aliases: []string{"u"},
|
||||
Usage: "update an `app`",
|
||||
ArgsUsage: "<app>",
|
||||
Action: a.update,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringSliceFlag{
|
||||
Name: "config,c",
|
||||
Usage: "route configuration",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "config",
|
||||
Usage: "manage your apps's function configs",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "set",
|
||||
Aliases: []string{"s"},
|
||||
Usage: "store a configuration key for this application",
|
||||
ArgsUsage: "<app> <key> <value>",
|
||||
Action: a.configSet,
|
||||
},
|
||||
{
|
||||
Name: "unset",
|
||||
Aliases: []string{"u"},
|
||||
Usage: "remove a configuration key for this application",
|
||||
ArgsUsage: "<app> <key>",
|
||||
Action: a.configUnset,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "list",
|
||||
Aliases: []string{"l"},
|
||||
Usage: "list all apps",
|
||||
Action: a.list,
|
||||
},
|
||||
{
|
||||
Name: "delete",
|
||||
Usage: "delete an app",
|
||||
Action: a.delete,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (a *appsCmd) list(c *cli.Context) error {
|
||||
resp, err := a.client.Apps.GetApps(&apiapps.GetAppsParams{
|
||||
Context: context.Background(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apiapps.GetAppsAppNotFound:
|
||||
return fmt.Errorf("error: %v", e.Payload.Error.Message)
|
||||
case *apiapps.GetAppsAppDefault:
|
||||
return fmt.Errorf("unexpected error: %v", e.Payload.Error.Message)
|
||||
case *apiapps.GetAppsDefault:
|
||||
// this is the one getting called, not sure what the one above is?
|
||||
return fmt.Errorf("unexpected error: %v", e.Payload.Error.Message)
|
||||
default:
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(resp.Payload.Apps) == 0 {
|
||||
fmt.Println("no apps found")
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, app := range resp.Payload.Apps {
|
||||
fmt.Println(app.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *appsCmd) create(c *cli.Context) error {
|
||||
body := &models.AppWrapper{App: &models.App{
|
||||
Name: c.Args().Get(0),
|
||||
Config: extractEnvConfig(c.StringSlice("config")),
|
||||
}}
|
||||
|
||||
resp, err := a.client.Apps.PostApps(&apiapps.PostAppsParams{
|
||||
Context: context.Background(),
|
||||
Body: body,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apiapps.PostAppsBadRequest:
|
||||
return fmt.Errorf("error: %v", e.Payload.Error.Message)
|
||||
case *apiapps.PostAppsConflict:
|
||||
return fmt.Errorf("error: %v", e.Payload.Error.Message)
|
||||
case *apiapps.PostAppsDefault:
|
||||
return fmt.Errorf("unexpected error: %v", e.Payload.Error.Message)
|
||||
default:
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Successfully created app: ", resp.Payload.App.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *appsCmd) update(c *cli.Context) error {
|
||||
appName := c.Args().First()
|
||||
|
||||
patchedApp := &models.App{
|
||||
Config: extractEnvConfig(c.StringSlice("config")),
|
||||
}
|
||||
|
||||
err := a.patchApp(appName, patchedApp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("app", appName, "updated")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *appsCmd) configSet(c *cli.Context) error {
|
||||
appName := c.Args().Get(0)
|
||||
key := c.Args().Get(1)
|
||||
value := c.Args().Get(2)
|
||||
|
||||
app := &models.App{
|
||||
Config: make(map[string]string),
|
||||
}
|
||||
|
||||
app.Config[key] = value
|
||||
|
||||
if err := a.patchApp(appName, app); err != nil {
|
||||
return fmt.Errorf("error updating app configuration: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println(appName, "updated", key, "with", value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *appsCmd) configUnset(c *cli.Context) error {
|
||||
appName := c.Args().Get(0)
|
||||
key := c.Args().Get(1)
|
||||
|
||||
app := &models.App{
|
||||
Config: make(map[string]string),
|
||||
}
|
||||
|
||||
app.Config[key] = ""
|
||||
|
||||
if err := a.patchApp(appName, app); err != nil {
|
||||
return fmt.Errorf("error updating app configuration: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("removed key '%s' from app '%s' \n", key, appName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *appsCmd) patchApp(appName string, app *models.App) error {
|
||||
_, err := a.client.Apps.PatchAppsApp(&apiapps.PatchAppsAppParams{
|
||||
Context: context.Background(),
|
||||
App: appName,
|
||||
Body: &models.AppWrapper{App: app},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apiapps.PatchAppsAppBadRequest:
|
||||
return errors.New(e.Payload.Error.Message)
|
||||
case *apiapps.PatchAppsAppNotFound:
|
||||
return errors.New(e.Payload.Error.Message)
|
||||
case *apiapps.PatchAppsAppDefault:
|
||||
return errors.New(e.Payload.Error.Message)
|
||||
default:
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *appsCmd) inspect(c *cli.Context) error {
|
||||
if c.Args().Get(0) == "" {
|
||||
return errors.New("error: missing app name after the inspect command")
|
||||
}
|
||||
|
||||
appName := c.Args().First()
|
||||
prop := c.Args().Get(1)
|
||||
|
||||
resp, err := a.client.Apps.GetAppsApp(&apiapps.GetAppsAppParams{
|
||||
Context: context.Background(),
|
||||
App: appName,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apiapps.GetAppsAppNotFound:
|
||||
return fmt.Errorf("error: %v", e.Payload.Error.Message)
|
||||
case *apiapps.GetAppsAppDefault:
|
||||
return fmt.Errorf("unexpected error: %v", e.Payload.Error.Message)
|
||||
default:
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", "\t")
|
||||
|
||||
if prop == "" {
|
||||
enc.Encode(resp.Payload.App)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: we really need to marshal it here just to
|
||||
// unmarshal as map[string]interface{}?
|
||||
data, err := json.Marshal(resp.Payload.App)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error inspect app: %v", err)
|
||||
}
|
||||
var inspect map[string]interface{}
|
||||
err = json.Unmarshal(data, &inspect)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error inspect app: %v", err)
|
||||
}
|
||||
|
||||
jq := jsonq.NewQuery(inspect)
|
||||
field, err := jq.Interface(strings.Split(prop, ".")...)
|
||||
if err != nil {
|
||||
return errors.New("failed to inspect that apps's field")
|
||||
}
|
||||
enc.Encode(field)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *appsCmd) delete(c *cli.Context) error {
|
||||
appName := c.Args().First()
|
||||
if appName == "" {
|
||||
return errors.New("error: deleting an app takes one argument, an app name")
|
||||
}
|
||||
|
||||
_, err := a.client.Apps.DeleteAppsApp(&apiapps.DeleteAppsAppParams{
|
||||
Context: context.Background(),
|
||||
App: appName,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apiapps.DeleteAppsAppNotFound:
|
||||
return errors.New(e.Payload.Error.Message)
|
||||
case *apiapps.DeleteAppsAppDefault:
|
||||
return errors.New(e.Payload.Error.Message)
|
||||
}
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("app", appName, "deleted")
|
||||
return nil
|
||||
}
|
||||
59
cli/build.go
59
cli/build.go
@@ -1,59 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func build() cli.Command {
|
||||
cmd := buildcmd{}
|
||||
flags := append([]cli.Flag{}, cmd.flags()...)
|
||||
return cli.Command{
|
||||
Name: "build",
|
||||
Usage: "build function version",
|
||||
Flags: flags,
|
||||
Action: cmd.build,
|
||||
}
|
||||
}
|
||||
|
||||
type buildcmd struct {
|
||||
verbose bool
|
||||
noCache bool
|
||||
}
|
||||
|
||||
func (b *buildcmd) flags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "v",
|
||||
Usage: "verbose mode",
|
||||
Destination: &b.verbose,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "no-cache",
|
||||
Usage: "Don't use docker cache",
|
||||
Destination: &b.noCache,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// build will take the found valid function and build it
|
||||
func (b *buildcmd) build(c *cli.Context) error {
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fn, err := findFuncfile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ff, err := buildfunc(fn, b.noCache)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Function %v built successfully.\n", ff.ImageName())
|
||||
return nil
|
||||
}
|
||||
102
cli/bump.go
102
cli/bump.go
@@ -1,102 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
bumper "github.com/giantswarm/semver-bump/bump"
|
||||
"github.com/giantswarm/semver-bump/storage"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
initialVersion = "0.0.1"
|
||||
)
|
||||
|
||||
func bump() cli.Command {
|
||||
cmd := bumpcmd{}
|
||||
flags := append([]cli.Flag{}, cmd.flags()...)
|
||||
return cli.Command{
|
||||
Name: "bump",
|
||||
Usage: "bump function version",
|
||||
Flags: flags,
|
||||
Action: cmd.bump,
|
||||
}
|
||||
}
|
||||
|
||||
type bumpcmd struct {
|
||||
verbose bool
|
||||
}
|
||||
|
||||
func (b *bumpcmd) flags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "v",
|
||||
Usage: "verbose mode",
|
||||
Destination: &b.verbose,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// bump will take the found valid function and bump its version
|
||||
func (b *bumpcmd) bump(c *cli.Context) error {
|
||||
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fn, err := findFuncfile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("bumping version for", fn)
|
||||
|
||||
funcfile, err := parsefuncfile(fn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
funcfile, err = bumpversion(*funcfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := storefuncfile(fn, funcfile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Bumped to version", funcfile.Version)
|
||||
return nil
|
||||
}
|
||||
|
||||
func bumpversion(funcfile funcfile) (*funcfile, error) {
|
||||
funcfile.Name = cleanImageName(funcfile.Name)
|
||||
if funcfile.Version == "" {
|
||||
funcfile.Version = initialVersion
|
||||
return &funcfile, nil
|
||||
}
|
||||
|
||||
s, err := storage.NewVersionStorage("local", funcfile.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
version := bumper.NewSemverBumper(s, "")
|
||||
newver, err := version.BumpPatchVersion("", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
funcfile.Version = newver.String()
|
||||
return &funcfile, nil
|
||||
}
|
||||
|
||||
func cleanImageName(name string) string {
|
||||
if i := strings.Index(name, ":"); i != -1 {
|
||||
name = name[:i]
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
99
cli/calls.go
99
cli/calls.go
@@ -1,99 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
client "github.com/fnproject/fn/cli/client"
|
||||
fnclient "github.com/funcy/functions_go/client"
|
||||
apicall "github.com/funcy/functions_go/client/call"
|
||||
"github.com/funcy/functions_go/models"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type callsCmd struct {
|
||||
client *fnclient.Functions
|
||||
}
|
||||
|
||||
func calls() cli.Command {
|
||||
c := callsCmd{client: client.APIClient()}
|
||||
|
||||
return cli.Command{
|
||||
Name: "calls",
|
||||
Usage: "manage function calls for apps",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "get",
|
||||
Aliases: []string{"g"},
|
||||
Usage: "get function call info per app",
|
||||
ArgsUsage: "<app> <call-id>",
|
||||
Action: c.get,
|
||||
},
|
||||
{
|
||||
Name: "list",
|
||||
Aliases: []string{"l"},
|
||||
Usage: "list all calls for specific app / route route is optional",
|
||||
ArgsUsage: "<app> [route]",
|
||||
Action: c.list,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func printCalls(calls []*models.Call) {
|
||||
for _, call := range calls {
|
||||
fmt.Println(fmt.Sprintf(
|
||||
"ID: %v\n"+
|
||||
"App: %v\n"+
|
||||
"Route: %v\n"+
|
||||
"Created At: %v\n"+
|
||||
"Started At: %v\n"+
|
||||
"Completed At: %v\n"+
|
||||
"Status: %v\n",
|
||||
call.ID, call.AppName, call.Path, call.CreatedAt,
|
||||
call.StartedAt, call.CompletedAt, call.Status))
|
||||
}
|
||||
}
|
||||
|
||||
func (call *callsCmd) get(ctx *cli.Context) error {
|
||||
app, callID := ctx.Args().Get(0), ctx.Args().Get(1)
|
||||
params := apicall.GetAppsAppCallsCallParams{
|
||||
Call: callID,
|
||||
App: app,
|
||||
Context: context.Background(),
|
||||
}
|
||||
resp, err := call.client.Call.GetAppsAppCallsCall(¶ms)
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apicall.GetAppsAppCallsCallNotFound:
|
||||
return fmt.Errorf("error: %v", e.Payload.Error.Message)
|
||||
default:
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
printCalls([]*models.Call{resp.Payload.Call})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (call *callsCmd) list(ctx *cli.Context) error {
|
||||
app := ctx.Args().Get(0)
|
||||
params := apicall.GetAppsAppCallsParams{
|
||||
App: app,
|
||||
Context: context.Background(),
|
||||
}
|
||||
if ctx.Args().Get(1) != "" {
|
||||
route := ctx.Args().Get(1)
|
||||
params.Route = &route
|
||||
}
|
||||
resp, err := call.client.Call.GetAppsAppCalls(¶ms)
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apicall.GetCallsCallNotFound:
|
||||
return fmt.Errorf("error: %v", e.Payload.Error.Message)
|
||||
default:
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
printCalls(resp.Payload.Calls)
|
||||
return nil
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"log"
|
||||
"net/url"
|
||||
|
||||
fnclient "github.com/funcy/functions_go/client"
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
const (
|
||||
envFnToken = "FN_TOKEN"
|
||||
)
|
||||
|
||||
func Host() string {
|
||||
apiURL := os.Getenv("API_URL")
|
||||
if apiURL == "" {
|
||||
apiURL = "http://localhost:8080"
|
||||
}
|
||||
|
||||
u, err := url.Parse(apiURL)
|
||||
if err != nil {
|
||||
log.Fatalln("Couldn't parse API URL:", err)
|
||||
}
|
||||
return u.Host
|
||||
}
|
||||
|
||||
func APIClient() *fnclient.Functions {
|
||||
transport := httptransport.New(Host(), "/v1", []string{"http"})
|
||||
if os.Getenv(envFnToken) != "" {
|
||||
transport.DefaultAuthentication = httptransport.BearerToken(os.Getenv(envFnToken))
|
||||
}
|
||||
|
||||
// create the API client, with the transport
|
||||
client := fnclient.New(transport, strfmt.Default)
|
||||
|
||||
return client
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const FN_CALL_ID = "Fn_call_id"
|
||||
|
||||
func EnvAsHeader(req *http.Request, selectedEnv []string) {
|
||||
detectedEnv := os.Environ()
|
||||
if len(selectedEnv) > 0 {
|
||||
detectedEnv = selectedEnv
|
||||
}
|
||||
|
||||
for _, e := range detectedEnv {
|
||||
kv := strings.Split(e, "=")
|
||||
name := kv[0]
|
||||
req.Header.Set(name, os.Getenv(name))
|
||||
}
|
||||
}
|
||||
|
||||
type apiErr struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type callID struct {
|
||||
CallID string `json:"call_id"`
|
||||
Error apiErr `json:"error"`
|
||||
}
|
||||
|
||||
func CallFN(u string, content io.Reader, output io.Writer, method string, env []string, includeCallID bool) error {
|
||||
if method == "" {
|
||||
if content == nil {
|
||||
method = "GET"
|
||||
} else {
|
||||
method = "POST"
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, u, content)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running route: %s", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
if len(env) > 0 {
|
||||
EnvAsHeader(req, env)
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running route: %s", err)
|
||||
}
|
||||
// for sync calls
|
||||
if call_id, found := resp.Header[FN_CALL_ID]; found {
|
||||
if includeCallID {
|
||||
fmt.Fprint(os.Stderr, fmt.Sprintf("Call ID: %v\n", call_id[0]))
|
||||
}
|
||||
io.Copy(output, resp.Body)
|
||||
} else {
|
||||
// for async calls and error discovering
|
||||
c := &callID{}
|
||||
err = json.NewDecoder(resp.Body).Decode(c)
|
||||
if err == nil {
|
||||
// decode would not fail in both cases:
|
||||
// - call id in body
|
||||
// - error in body
|
||||
// that's why we need to check values of attributes
|
||||
if c.CallID != "" {
|
||||
fmt.Fprint(os.Stderr, fmt.Sprintf("Call ID: %v\n", c.CallID))
|
||||
} else {
|
||||
fmt.Fprint(output, fmt.Sprintf("Error: %v\n", c.Error.Message))
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
// TODO: parse out error message
|
||||
return fmt.Errorf("error calling function: status %v", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
305
cli/common.go
305
cli/common.go
@@ -1,305 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
|
||||
"github.com/fnproject/fn/cli/langs"
|
||||
)
|
||||
|
||||
const (
|
||||
functionsDockerImage = "fnproject/functions"
|
||||
minRequiredDockerVersion = "17.5.0"
|
||||
envFnRegistry = "FN_REGISTRY"
|
||||
)
|
||||
|
||||
type HasRegistry interface {
|
||||
Registry() string
|
||||
}
|
||||
|
||||
func setRegistryEnv(hr HasRegistry) {
|
||||
if hr.Registry() != "" {
|
||||
err := os.Setenv(envFnRegistry, hr.Registry())
|
||||
if err != nil {
|
||||
log.Fatalf("Couldn't set %s env var: %v\n", envFnRegistry, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildfunc(fn string, noCache bool) (*funcfile, error) {
|
||||
funcfile, err := parsefuncfile(fn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if funcfile.Version == "" {
|
||||
funcfile, err = bumpversion(*funcfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := storefuncfile(fn, funcfile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
funcfile, err = parsefuncfile(fn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := localbuild(fn, funcfile.Build); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := dockerbuild(fn, funcfile, noCache); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return funcfile, nil
|
||||
}
|
||||
|
||||
func localbuild(path string, steps []string) error {
|
||||
for _, cmd := range steps {
|
||||
exe := exec.Command("/bin/sh", "-c", cmd)
|
||||
exe.Dir = filepath.Dir(path)
|
||||
if err := exe.Run(); err != nil {
|
||||
return fmt.Errorf("error running command %v (%v)", cmd, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dockerbuild(path string, ff *funcfile, noCache bool) error {
|
||||
err := dockerVersionCheck()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dir := filepath.Dir(path)
|
||||
|
||||
var helper langs.LangHelper
|
||||
dockerfile := filepath.Join(dir, "Dockerfile")
|
||||
if !exists(dockerfile) {
|
||||
helper = langs.GetLangHelper(ff.Runtime)
|
||||
if helper == nil {
|
||||
return fmt.Errorf("Cannot build, no language helper found for %v", ff.Runtime)
|
||||
}
|
||||
dockerfile, err = writeTmpDockerfile(helper, dir, ff)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(dockerfile)
|
||||
if helper.HasPreBuild() {
|
||||
err := helper.PreBuild()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Building image %v\n", ff.ImageName())
|
||||
|
||||
cancel := make(chan os.Signal, 3)
|
||||
signal.Notify(cancel, os.Interrupt) // and others perhaps
|
||||
defer signal.Stop(cancel)
|
||||
|
||||
result := make(chan error, 1)
|
||||
|
||||
go func(done chan<- error) {
|
||||
args := []string{
|
||||
"build",
|
||||
"-t", ff.ImageName(),
|
||||
"-f", dockerfile,
|
||||
}
|
||||
if noCache {
|
||||
args = append(args, "--no-cache")
|
||||
}
|
||||
args = append(args,
|
||||
"--build-arg", "HTTP_PROXY",
|
||||
"--build-arg", "HTTPS_PROXY",
|
||||
".")
|
||||
cmd := exec.Command("docker", args...)
|
||||
cmd.Dir = dir
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
done <- cmd.Run()
|
||||
}(result)
|
||||
|
||||
select {
|
||||
case err := <-result:
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running docker build: %v", err)
|
||||
}
|
||||
case signal := <-cancel:
|
||||
return fmt.Errorf("build cancelled on signal %v", signal)
|
||||
}
|
||||
|
||||
if helper != nil {
|
||||
err := helper.AfterBuild()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dockerVersionCheck() error {
|
||||
out, err := exec.Command("docker", "version", "--format", "{{.Server.Version}}").Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not check Docker version: %v", err)
|
||||
}
|
||||
// dev / test builds append '-ce', trim this
|
||||
trimmed := strings.TrimRightFunc(string(out), func(r rune) bool { return r != '.' && !unicode.IsDigit(r) })
|
||||
|
||||
v, err := semver.NewVersion(trimmed)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not check Docker version: %v", err)
|
||||
}
|
||||
vMin, err := semver.NewVersion(minRequiredDockerVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("our bad, sorry... please make an issue.", err)
|
||||
}
|
||||
if v.LessThan(*vMin) {
|
||||
return fmt.Errorf("please upgrade your version of Docker to %s or greater", minRequiredDockerVersion)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func exists(name string) bool {
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func writeTmpDockerfile(helper langs.LangHelper, dir string, ff *funcfile) (string, error) {
|
||||
if ff.Entrypoint == "" && ff.Cmd == "" {
|
||||
return "", errors.New("entrypoint and cmd are missing, you must provide one or the other")
|
||||
}
|
||||
|
||||
fd, err := ioutil.TempFile(dir, "Dockerfile")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
// multi-stage build: https://medium.com/travis-on-docker/multi-stage-docker-builds-for-creating-tiny-go-images-e0e1867efe5a
|
||||
dfLines := []string{}
|
||||
if helper.IsMultiStage() {
|
||||
// build stage
|
||||
dfLines = append(dfLines, fmt.Sprintf("FROM %s as build-stage", helper.BuildFromImage()))
|
||||
} else {
|
||||
dfLines = append(dfLines, fmt.Sprintf("FROM %s", helper.BuildFromImage()))
|
||||
}
|
||||
dfLines = append(dfLines, "WORKDIR /function")
|
||||
dfLines = append(dfLines, helper.DockerfileBuildCmds()...)
|
||||
if helper.IsMultiStage() {
|
||||
// final stage
|
||||
dfLines = append(dfLines, fmt.Sprintf("FROM %s", helper.RunFromImage()))
|
||||
dfLines = append(dfLines, "WORKDIR /function")
|
||||
dfLines = append(dfLines, helper.DockerfileCopyCmds()...)
|
||||
}
|
||||
if ff.Entrypoint != "" {
|
||||
dfLines = append(dfLines, fmt.Sprintf("ENTRYPOINT [%s]", stringToSlice(ff.Entrypoint)))
|
||||
}
|
||||
if ff.Cmd != "" {
|
||||
dfLines = append(dfLines, fmt.Sprintf("CMD [%s]", stringToSlice(ff.Cmd)))
|
||||
}
|
||||
err = writeLines(fd, dfLines)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fd.Name(), err
|
||||
}
|
||||
|
||||
func writeLines(w io.Writer, lines []string) error {
|
||||
writer := bufio.NewWriter(w)
|
||||
for _, l := range lines {
|
||||
_, err := writer.WriteString(l + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
writer.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
func stringToSlice(in string) string {
|
||||
epvals := strings.Fields(in)
|
||||
var buffer bytes.Buffer
|
||||
for i, s := range epvals {
|
||||
if i > 0 {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString("\"")
|
||||
buffer.WriteString(s)
|
||||
buffer.WriteString("\"")
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func extractEnvConfig(configs []string) map[string]string {
|
||||
c := make(map[string]string)
|
||||
for _, v := range configs {
|
||||
kv := strings.SplitN(v, "=", 2)
|
||||
if len(kv) == 2 {
|
||||
c[kv[0]] = os.ExpandEnv(kv[1])
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func dockerpush(ff *funcfile) error {
|
||||
err := validImageName(ff.ImageName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Pushing %v to docker registry...", ff.ImageName())
|
||||
cmd := exec.Command("docker", "push", ff.ImageName())
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("error running docker push: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validImageName(n string) error {
|
||||
// must have at least owner name and a tag
|
||||
split := strings.Split(n, ":")
|
||||
if len(split) < 2 {
|
||||
return errors.New("image name must have a tag")
|
||||
}
|
||||
split2 := strings.Split(split[0], "/")
|
||||
if len(split2) < 2 {
|
||||
return errors.New("image name must have an owner and name, eg: username/myfunc. Be sure to set FN_REGISTRY env var or pass in --registry.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func appNamePath(img string) (string, string) {
|
||||
sep := strings.Index(img, "/")
|
||||
if sep < 0 {
|
||||
return "", ""
|
||||
}
|
||||
tag := strings.Index(img[sep:], ":")
|
||||
if tag < 0 {
|
||||
tag = len(img[sep:])
|
||||
}
|
||||
return img[:sep], img[sep : sep+tag]
|
||||
}
|
||||
224
cli/deploy.go
224
cli/deploy.go
@@ -1,224 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
client "github.com/fnproject/fn/cli/client"
|
||||
functions "github.com/funcy/functions_go"
|
||||
"github.com/funcy/functions_go/models"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func deploy() cli.Command {
|
||||
cmd := deploycmd{
|
||||
RoutesApi: functions.NewRoutesApi(),
|
||||
}
|
||||
var flags []cli.Flag
|
||||
flags = append(flags, cmd.flags()...)
|
||||
return cli.Command{
|
||||
Name: "deploy",
|
||||
ArgsUsage: "<appName>",
|
||||
Usage: "deploys a function to the functions server. (bumps, build, pushes and updates route)",
|
||||
Flags: flags,
|
||||
Action: cmd.scan,
|
||||
}
|
||||
}
|
||||
|
||||
type deploycmd struct {
|
||||
appName string
|
||||
*functions.RoutesApi
|
||||
|
||||
wd string
|
||||
verbose bool
|
||||
incremental bool
|
||||
skippush bool
|
||||
noCache bool
|
||||
registry string
|
||||
}
|
||||
|
||||
func (cmd *deploycmd) Registry() string {
|
||||
return cmd.registry
|
||||
}
|
||||
|
||||
func (p *deploycmd) flags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "v",
|
||||
Usage: "verbose mode",
|
||||
Destination: &p.verbose,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "d",
|
||||
Usage: "working directory",
|
||||
Destination: &p.wd,
|
||||
EnvVar: "WORK_DIR",
|
||||
// Value: "./",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "i",
|
||||
Usage: "uses incremental building",
|
||||
Destination: &p.incremental,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "no-cache",
|
||||
Usage: "Don't use Docker cache for the build",
|
||||
Destination: &p.noCache,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "skip-push",
|
||||
Usage: "does not push Docker built images onto Docker Hub - useful for local development.",
|
||||
Destination: &p.skippush,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "registry",
|
||||
Usage: "Sets the Docker owner for images and optionally the registry. This will be prefixed to your function name for pushing to Docker registries. eg: `--registry username` will set your Docker Hub owner. `--registry registry.hub.docker.com/username` will set the registry and owner.",
|
||||
Destination: &p.registry,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *deploycmd) scan(c *cli.Context) error {
|
||||
p.appName = c.Args().First()
|
||||
|
||||
var walked bool
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalln("Couldn't get working directory:", err)
|
||||
}
|
||||
setRegistryEnv(p)
|
||||
|
||||
err = filepath.Walk(wd, func(path string, info os.FileInfo, err error) error {
|
||||
if path != wd && info.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
if !isFuncfile(path, info) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.incremental && !isstale(path) {
|
||||
return nil
|
||||
}
|
||||
|
||||
e := p.deploy(c, path)
|
||||
if err != nil {
|
||||
fmt.Println(path, e)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
os.Chtimes(path, now, now)
|
||||
walked = true
|
||||
return e
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("error: %s\n", err)
|
||||
}
|
||||
|
||||
if !walked {
|
||||
return errors.New("No function file found.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// deploy will perform several actions to deploy to an functions server.
|
||||
// Parse functions file, bump version, build image, push to registry, and
|
||||
// finally it will update function's route. Optionally,
|
||||
// the route can be overriden inside the functions file.
|
||||
func (p *deploycmd) deploy(c *cli.Context, funcFilePath string) error {
|
||||
funcFileName := path.Base(funcFilePath)
|
||||
|
||||
err := c.App.Command("bump").Run(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
funcfile, err := buildfunc(funcFileName, p.noCache)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if funcfile.Path == "" {
|
||||
funcfile.Path = "/" + path.Base(path.Dir(funcFilePath))
|
||||
}
|
||||
|
||||
if p.skippush {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := dockerpush(funcfile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.route(c, funcfile)
|
||||
}
|
||||
|
||||
func (p *deploycmd) route(c *cli.Context, ff *funcfile) error {
|
||||
fmt.Printf("Updating route %s using image %s...\n", ff.Path, ff.ImageName())
|
||||
if err := resetBasePath(p.Configuration); err != nil {
|
||||
return fmt.Errorf("error setting endpoint: %v", err)
|
||||
}
|
||||
|
||||
routesCmd := routesCmd{client: client.APIClient()}
|
||||
rt := &models.Route{}
|
||||
if err := routeWithFuncFile(ff, rt); err != nil {
|
||||
return fmt.Errorf("error getting route with funcfile: %s", err)
|
||||
}
|
||||
return routesCmd.putRoute(c, p.appName, ff.Path, rt)
|
||||
}
|
||||
|
||||
func expandEnvConfig(configs map[string]string) map[string]string {
|
||||
for k, v := range configs {
|
||||
configs[k] = os.ExpandEnv(v)
|
||||
}
|
||||
return configs
|
||||
}
|
||||
|
||||
func isFuncfile(path string, info os.FileInfo) bool {
|
||||
if info.IsDir() {
|
||||
return false
|
||||
}
|
||||
|
||||
basefn := filepath.Base(path)
|
||||
for _, fn := range validfn {
|
||||
if basefn == fn {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Theory of operation: this takes an optimistic approach to detect whether a
|
||||
// package must be rebuild/bump/deployed. It loads for all files mtime's and
|
||||
// compare with functions.json own mtime. If any file is younger than
|
||||
// functions.json, it triggers a rebuild.
|
||||
// The problem with this approach is that depending on the OS running it, the
|
||||
// time granularity of these timestamps might lead to false negatives - that is
|
||||
// a package that is stale but it is not recompiled. A more elegant solution
|
||||
// could be applied here, like https://golang.org/src/cmd/go/pkg.go#L1111
|
||||
func isstale(path string) bool {
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
fnmtime := fi.ModTime()
|
||||
dir := filepath.Dir(path)
|
||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if info.ModTime().After(fnmtime) {
|
||||
return errors.New("found stale package")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return err != nil
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
HOST=$(/sbin/ip route|awk '/default/ { print $3 }')
|
||||
|
||||
echo "$HOST default localhost localhost.local" > /etc/hosts
|
||||
|
||||
/fn "$@"
|
||||
@@ -1,13 +0,0 @@
|
||||
package main
|
||||
|
||||
type notFoundError struct {
|
||||
S string
|
||||
}
|
||||
|
||||
func (e *notFoundError) Error() string {
|
||||
return e.S
|
||||
}
|
||||
|
||||
func newNotFoundError(s string) *notFoundError {
|
||||
return ¬FoundError{S: s}
|
||||
}
|
||||
169
cli/funcfile.go
169
cli/funcfile.go
@@ -1,169 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
validfn = [...]string{
|
||||
"func.yaml",
|
||||
"func.yml",
|
||||
"func.json",
|
||||
}
|
||||
|
||||
errUnexpectedFileFormat = errors.New("unexpected file format for function file")
|
||||
)
|
||||
|
||||
type inputMap struct {
|
||||
Body interface{}
|
||||
}
|
||||
type outputMap struct {
|
||||
Body interface{}
|
||||
}
|
||||
|
||||
type fftest struct {
|
||||
Name string `yaml:"name,omitempty" json:"name,omitempty"`
|
||||
Input *inputMap `yaml:"input,omitempty" json:"input,omitempty"`
|
||||
Output *outputMap `yaml:"outoutput,omitempty" json:"output,omitempty"`
|
||||
Err *string `yaml:"err,omitempty" json:"err,omitempty"`
|
||||
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
|
||||
}
|
||||
|
||||
type funcfile struct {
|
||||
Name string `yaml:"name,omitempty" json:"name,omitempty"`
|
||||
Version string `yaml:"version,omitempty" json:"version,omitempty"`
|
||||
Runtime string `yaml:"runtime,omitempty" json:"runtime,omitempty"`
|
||||
Entrypoint string `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty"`
|
||||
Cmd string `yaml:"cmd,omitempty" json:"cmd,omitempty"`
|
||||
Build []string `yaml:"build,omitempty" json:"build,omitempty"`
|
||||
Tests []fftest `yaml:"tests,omitempty" json:"tests,omitempty"`
|
||||
|
||||
// route specific
|
||||
Type string `yaml:"type,omitempty" json:"type,omitempty"`
|
||||
Memory uint64 `yaml:"memory,omitempty" json:"memory,omitempty"`
|
||||
Format string `yaml:"format,omitempty" json:"format,omitempty"`
|
||||
Timeout *int32 `yaml:"timeout,omitempty" json:"timeout,omitempty"`
|
||||
Path string `yaml:"path,omitempty" json:"path,omitempty"`
|
||||
Config map[string]string `yaml:"config,omitempty" json:"config,omitempty"`
|
||||
Headers map[string][]string `yaml:"headers,omitempty" json:"headers,omitempty"`
|
||||
IDLETimeout *int32 `yaml:"idle_timeout,omitempty" json:"idle_timeout,omitempty"`
|
||||
}
|
||||
|
||||
func (ff *funcfile) ImageName() string {
|
||||
fname := ff.Name
|
||||
if !strings.Contains(fname, "/") {
|
||||
// then we'll prefix FN_REGISTRY
|
||||
reg := os.Getenv(envFnRegistry)
|
||||
if reg != "" {
|
||||
if reg[len(reg)-1] != '/' {
|
||||
reg += "/"
|
||||
}
|
||||
fname = fmt.Sprintf("%s%s", reg, fname)
|
||||
}
|
||||
}
|
||||
if ff.Version != "" {
|
||||
fname = fmt.Sprintf("%s:%s", fname, ff.Version)
|
||||
}
|
||||
return fname
|
||||
}
|
||||
|
||||
func (ff *funcfile) RuntimeTag() (runtime, tag string) {
|
||||
if ff.Runtime == "" {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
rt := ff.Runtime
|
||||
tagpos := strings.Index(rt, ":")
|
||||
if tagpos == -1 {
|
||||
return rt, ""
|
||||
}
|
||||
|
||||
return rt[:tagpos], rt[tagpos+1:]
|
||||
}
|
||||
|
||||
func findFuncfile(path string) (string, error) {
|
||||
for _, fn := range validfn {
|
||||
fullfn := filepath.Join(path, fn)
|
||||
if exists(fullfn) {
|
||||
return fullfn, nil
|
||||
}
|
||||
}
|
||||
return "", newNotFoundError("could not find function file")
|
||||
}
|
||||
|
||||
func loadFuncfile() (*funcfile, error) {
|
||||
fn, err := findFuncfile(".")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parsefuncfile(fn)
|
||||
}
|
||||
|
||||
func parsefuncfile(path string) (*funcfile, error) {
|
||||
ext := filepath.Ext(path)
|
||||
switch ext {
|
||||
case ".json":
|
||||
return decodeFuncfileJSON(path)
|
||||
case ".yaml", ".yml":
|
||||
return decodeFuncfileYAML(path)
|
||||
}
|
||||
return nil, errUnexpectedFileFormat
|
||||
}
|
||||
|
||||
func storefuncfile(path string, ff *funcfile) error {
|
||||
ext := filepath.Ext(path)
|
||||
switch ext {
|
||||
case ".json":
|
||||
return encodeFuncfileJSON(path, ff)
|
||||
case ".yaml", ".yml":
|
||||
return encodeFuncfileYAML(path, ff)
|
||||
}
|
||||
return errUnexpectedFileFormat
|
||||
}
|
||||
|
||||
func decodeFuncfileJSON(path string) (*funcfile, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not open %s for parsing. Error: %v", path, err)
|
||||
}
|
||||
ff := &funcfile{}
|
||||
// ff.Route = &fnmodels.Route{}
|
||||
err = json.NewDecoder(f).Decode(ff)
|
||||
// ff := fff.MakeFuncFile()
|
||||
return ff, err
|
||||
}
|
||||
|
||||
func decodeFuncfileYAML(path string) (*funcfile, error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not open %s for parsing. Error: %v", path, err)
|
||||
}
|
||||
ff := &funcfile{}
|
||||
err = yaml.Unmarshal(b, ff)
|
||||
// ff := fff.MakeFuncFile()
|
||||
return ff, err
|
||||
}
|
||||
|
||||
func encodeFuncfileJSON(path string, ff *funcfile) error {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open %s for encoding. Error: %v", path, err)
|
||||
}
|
||||
return json.NewEncoder(f).Encode(ff)
|
||||
}
|
||||
|
||||
func encodeFuncfileYAML(path string, ff *funcfile) error {
|
||||
b, err := yaml.Marshal(ff)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not encode function file. Error: %v", err)
|
||||
}
|
||||
return ioutil.WriteFile(path, b, os.FileMode(0644))
|
||||
}
|
||||
181
cli/glide.lock
generated
181
cli/glide.lock
generated
@@ -1,181 +0,0 @@
|
||||
hash: fc16e358787292cb0b0d7f71adf4f0685b5994ca1c81788fdd056de21ebe7e55
|
||||
updated: 2017-08-18T21:34:47.686061147+03:00
|
||||
imports:
|
||||
- name: github.com/asaskevich/govalidator
|
||||
version: aa5cce4a76edb1a5acecab1870c17abbffb5419e
|
||||
- name: github.com/aws/aws-sdk-go
|
||||
version: b33b08175d12b795daa7726c5577b39d06a2551c
|
||||
subpackages:
|
||||
- aws
|
||||
- aws/awserr
|
||||
- aws/awsutil
|
||||
- aws/client
|
||||
- aws/client/metadata
|
||||
- aws/corehandlers
|
||||
- aws/credentials
|
||||
- aws/credentials/ec2rolecreds
|
||||
- aws/credentials/endpointcreds
|
||||
- aws/credentials/stscreds
|
||||
- aws/defaults
|
||||
- aws/ec2metadata
|
||||
- aws/endpoints
|
||||
- aws/request
|
||||
- aws/session
|
||||
- aws/signer/v4
|
||||
- internal/shareddefaults
|
||||
- private/protocol
|
||||
- private/protocol/json/jsonutil
|
||||
- private/protocol/jsonrpc
|
||||
- private/protocol/query
|
||||
- private/protocol/query/queryutil
|
||||
- private/protocol/rest
|
||||
- private/protocol/restjson
|
||||
- private/protocol/xml/xmlutil
|
||||
- service/lambda
|
||||
- service/sts
|
||||
- name: github.com/Azure/go-ansiterm
|
||||
version: fa152c58bc15761d0200cb75fe958b89a9d4888e
|
||||
subpackages:
|
||||
- winterm
|
||||
- name: github.com/coreos/go-semver
|
||||
version: 8ab6407b697782a06568d4b7f1db25550ec2e4c6
|
||||
subpackages:
|
||||
- semver
|
||||
- name: github.com/docker/docker
|
||||
version: 89658bed64c2a8fe05a978e5b87dbec409d57a0f
|
||||
subpackages:
|
||||
- pkg/jsonlog
|
||||
- pkg/term
|
||||
- pkg/term/windows
|
||||
- name: github.com/docker/go-units
|
||||
version: 0dadbb0345b35ec7ef35e228dabb8de89a65bf52
|
||||
- name: github.com/funcy/functions_go
|
||||
version: e046aa4ca1f1028a04fc51395297ff07515cb0b6
|
||||
subpackages:
|
||||
- client
|
||||
- client/apps
|
||||
- client/call
|
||||
- client/operations
|
||||
- client/routes
|
||||
- models
|
||||
- name: github.com/giantswarm/semver-bump
|
||||
version: 88e6c9f2fe390c48839eaba32490fd09cb3b581c
|
||||
subpackages:
|
||||
- bump
|
||||
- storage
|
||||
- name: github.com/go-ini/ini
|
||||
version: 3d73f4b845efdf9989fffd4b4e562727744a34ba
|
||||
- name: github.com/go-openapi/analysis
|
||||
version: 0473cb67199f68b8b7d90e641afd9e79ad36b851
|
||||
- name: github.com/go-openapi/errors
|
||||
version: 03cfca65330da08a5a440053faf994a3c682b5bf
|
||||
- name: github.com/go-openapi/jsonpointer
|
||||
version: 779f45308c19820f1a69e9a4cd965f496e0da10f
|
||||
- name: github.com/go-openapi/jsonreference
|
||||
version: 36d33bfe519efae5632669801b180bf1a245da3b
|
||||
- name: github.com/go-openapi/loads
|
||||
version: a80dea3052f00e5f032e860dd7355cd0cc67e24d
|
||||
- name: github.com/go-openapi/runtime
|
||||
version: 2e9e988df6c290425033bacd425e008950c96be6
|
||||
subpackages:
|
||||
- client
|
||||
- name: github.com/go-openapi/spec
|
||||
version: e81a13315ac92ce3e73075856c5cd50301695405
|
||||
- name: github.com/go-openapi/strfmt
|
||||
version: 93a31ef21ac23f317792fff78f9539219dd74619
|
||||
- name: github.com/go-openapi/swag
|
||||
version: f3f9494671f93fcff853e3c6e9e948b3eb71e590
|
||||
- name: github.com/go-openapi/validate
|
||||
version: 035dcd74f1f61e83debe1c22950dc53556e7e4b2
|
||||
- name: github.com/go-resty/resty
|
||||
version: f214013978f4ea5632ef88a5371b2028699a9d19
|
||||
- name: github.com/jmespath/go-jmespath
|
||||
version: bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
||||
- name: github.com/jmoiron/jsonq
|
||||
version: e874b168d07ecc7808bc950a17998a8aa3141d82
|
||||
- name: github.com/juju/errgo
|
||||
version: 08cceb5d0b5331634b9826762a8fd53b29b86ad8
|
||||
subpackages:
|
||||
- errors
|
||||
- name: github.com/mailru/easyjson
|
||||
version: 44c0351a5bc860bcb2608d54aa03ea686c4e7b25
|
||||
subpackages:
|
||||
- buffer
|
||||
- jlexer
|
||||
- jwriter
|
||||
- name: github.com/mitchellh/mapstructure
|
||||
version: d0303fe809921458f417bcf828397a65db30a7e4
|
||||
- name: github.com/moby/moby
|
||||
version: 90d35abf7b3535c1c319c872900fbd76374e521c
|
||||
subpackages:
|
||||
- pkg/jsonmessage
|
||||
- name: github.com/Nvveen/Gotty
|
||||
version: cd527374f1e5bff4938207604a14f2e38a9cf512
|
||||
- name: github.com/onsi/gomega
|
||||
version: c893efa28eb45626cdaa76c9f653b62488858837
|
||||
subpackages:
|
||||
- format
|
||||
- internal/assertion
|
||||
- internal/asyncassertion
|
||||
- internal/oraclematcher
|
||||
- internal/testingtsupport
|
||||
- matchers
|
||||
- matchers/support/goraph/bipartitegraph
|
||||
- matchers/support/goraph/edge
|
||||
- matchers/support/goraph/node
|
||||
- matchers/support/goraph/util
|
||||
- types
|
||||
- name: github.com/PuerkitoBio/purell
|
||||
version: b938d81255b5473c57635324295cb0fe398c7a58
|
||||
- name: github.com/PuerkitoBio/urlesc
|
||||
version: bbf7a2afc14f93e1e0a5c06df524fbd75e5031e5
|
||||
- name: github.com/Sirupsen/logrus
|
||||
version: ba1b36c82c5e05c4f912a88eab0dcd91a171688f
|
||||
repo: https://github.com/sirupsen/logrus
|
||||
vcs: git
|
||||
- name: github.com/sirupsen/logrus
|
||||
version: ba1b36c82c5e05c4f912a88eab0dcd91a171688f
|
||||
- name: github.com/urfave/cli
|
||||
version: 4b90d79a682b4bf685762c7452db20f2a676ecb2
|
||||
- name: golang.org/x/net
|
||||
version: f315505cf3349909cdf013ea56690da34e96a451
|
||||
subpackages:
|
||||
- context
|
||||
- context/ctxhttp
|
||||
- html
|
||||
- html/atom
|
||||
- html/charset
|
||||
- idna
|
||||
- publicsuffix
|
||||
- name: golang.org/x/sys
|
||||
version: 0b25a408a50076fbbcae6b7ac0ea5fbb0b085e79
|
||||
subpackages:
|
||||
- unix
|
||||
- name: golang.org/x/text
|
||||
version: 210eee5cf7323015d097341bcf7166130d001cd8
|
||||
subpackages:
|
||||
- encoding
|
||||
- encoding/charmap
|
||||
- encoding/htmlindex
|
||||
- encoding/internal
|
||||
- encoding/internal/identifier
|
||||
- encoding/japanese
|
||||
- encoding/korean
|
||||
- encoding/simplifiedchinese
|
||||
- encoding/traditionalchinese
|
||||
- encoding/unicode
|
||||
- internal/tag
|
||||
- internal/utf8internal
|
||||
- language
|
||||
- runes
|
||||
- transform
|
||||
- unicode/norm
|
||||
- width
|
||||
- name: gopkg.in/mgo.v2
|
||||
version: 3f83fa5005286a7fe593b055f0d7771a7dce4655
|
||||
subpackages:
|
||||
- bson
|
||||
- internal/json
|
||||
- name: gopkg.in/yaml.v2
|
||||
version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b
|
||||
testImports: []
|
||||
@@ -1,45 +0,0 @@
|
||||
package: github.com/fnproject/fn/cli
|
||||
import:
|
||||
- package: github.com/Sirupsen/logrus
|
||||
repo: https://github.com/sirupsen/logrus
|
||||
vcs: git
|
||||
version: v0.11.5
|
||||
- package: github.com/sirupsen/logrus
|
||||
version: v0.11.5
|
||||
- package: github.com/aws/aws-sdk-go
|
||||
version: ^1.8.36
|
||||
subpackages:
|
||||
- aws
|
||||
- aws/credentials
|
||||
- aws/session
|
||||
- service/lambda
|
||||
- package: github.com/coreos/go-semver
|
||||
version: ^0.2.0
|
||||
subpackages:
|
||||
- semver
|
||||
- package: github.com/funcy/functions_go
|
||||
version: ^0.1.36
|
||||
subpackages:
|
||||
- client
|
||||
- client/apps
|
||||
- client/call
|
||||
- client/routes
|
||||
- models
|
||||
- package: github.com/giantswarm/semver-bump
|
||||
version: ^1.1.1
|
||||
subpackages:
|
||||
- bump
|
||||
- storage
|
||||
- package: github.com/go-openapi/runtime
|
||||
subpackages:
|
||||
- client
|
||||
- package: github.com/go-openapi/strfmt
|
||||
- package: github.com/jmoiron/jsonq
|
||||
- package: github.com/moby/moby
|
||||
version: ^17.5.0-ce-rc3
|
||||
subpackages:
|
||||
- pkg/jsonmessage
|
||||
- package: github.com/urfave/cli
|
||||
# version: ^1.19.1
|
||||
version: master
|
||||
- package: gopkg.in/yaml.v2
|
||||
@@ -1,26 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/funcy/functions_go"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type imagesCmd struct {
|
||||
*functions.AppsApi
|
||||
}
|
||||
|
||||
func images() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "images",
|
||||
Usage: "manage function images",
|
||||
Subcommands: []cli.Command{
|
||||
build(),
|
||||
deploy(),
|
||||
bump(),
|
||||
call(),
|
||||
push(),
|
||||
run(),
|
||||
testfn(),
|
||||
},
|
||||
}
|
||||
}
|
||||
223
cli/init.go
223
cli/init.go
@@ -1,223 +0,0 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
usage: fn init <name>
|
||||
|
||||
If there's a Dockerfile found, this will generate the basic file with just the image name. exit
|
||||
It will then try to decipher the runtime based on the files in the current directory, if it can't figure it out, it will ask.
|
||||
It will then take a best guess for what the entrypoint will be based on the language, it it can't guess, it will ask.
|
||||
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/fnproject/fn/cli/langs"
|
||||
"github.com/funcy/functions_go/models"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
fileExtToRuntime = map[string]string{
|
||||
".go": "go",
|
||||
".js": "node",
|
||||
".rb": "ruby",
|
||||
".py": "python",
|
||||
".php": "php",
|
||||
".rs": "rust",
|
||||
".cs": "dotnet",
|
||||
".fs": "dotnet",
|
||||
".java": "java",
|
||||
}
|
||||
|
||||
fnInitRuntimes []string
|
||||
)
|
||||
|
||||
func init() {
|
||||
for rt := range fileExtToRuntime {
|
||||
fnInitRuntimes = append(fnInitRuntimes, rt)
|
||||
}
|
||||
}
|
||||
|
||||
type initFnCmd struct {
|
||||
force bool
|
||||
funcfile
|
||||
}
|
||||
|
||||
func initFlags(a *initFnCmd) []cli.Flag {
|
||||
fgs := []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "force",
|
||||
Usage: "overwrite existing func.yaml",
|
||||
Destination: &a.force,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "runtime",
|
||||
Usage: "choose an existing runtime - " + strings.Join(fnInitRuntimes, ", "),
|
||||
Destination: &a.Runtime,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "entrypoint",
|
||||
Usage: "entrypoint is the command to run to start this function - equivalent to Dockerfile ENTRYPOINT.",
|
||||
Destination: &a.Entrypoint,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "cmd",
|
||||
Usage: "command to run to start this function - equivalent to Dockerfile CMD.",
|
||||
Destination: &a.Entrypoint,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "version",
|
||||
Usage: "function version",
|
||||
Destination: &a.Version,
|
||||
Value: initialVersion,
|
||||
},
|
||||
}
|
||||
|
||||
return append(fgs, routeFlags...)
|
||||
}
|
||||
|
||||
func initFn() cli.Command {
|
||||
a := &initFnCmd{}
|
||||
// funcfile := &funcfile{}
|
||||
|
||||
return cli.Command{
|
||||
Name: "init",
|
||||
Usage: "create a local func.yaml file",
|
||||
Description: "Creates a func.yaml file in the current directory.",
|
||||
ArgsUsage: "[FUNCTION_NAME]",
|
||||
Action: a.init,
|
||||
Flags: initFlags(a),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *initFnCmd) init(c *cli.Context) error {
|
||||
|
||||
rt := &models.Route{}
|
||||
routeWithFlags(c, rt)
|
||||
|
||||
if !a.force {
|
||||
ff, err := loadFuncfile()
|
||||
if _, ok := err.(*notFoundError); !ok && err != nil {
|
||||
return err
|
||||
}
|
||||
if ff != nil {
|
||||
return errors.New("Function file already exists")
|
||||
}
|
||||
}
|
||||
|
||||
err := a.buildFuncFile(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
runtimeSpecified := a.Runtime != ""
|
||||
|
||||
if runtimeSpecified {
|
||||
err := a.generateBoilerplate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ff := a.funcfile
|
||||
|
||||
_, path := appNamePath(ff.ImageName())
|
||||
ff.Path = path
|
||||
|
||||
if err := encodeFuncfileYAML("func.yaml", &ff); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("func.yaml created")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *initFnCmd) generateBoilerplate() error {
|
||||
helper := langs.GetLangHelper(a.Runtime)
|
||||
if helper != nil && helper.HasBoilerplate() {
|
||||
if err := helper.GenerateBoilerplate(); err != nil {
|
||||
if err == langs.ErrBoilerplateExists {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
fmt.Println("function boilerplate generated.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *initFnCmd) buildFuncFile(c *cli.Context) error {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error detecting current working directory: %v", err)
|
||||
}
|
||||
|
||||
a.Name = c.Args().First()
|
||||
// if a.name == "" {
|
||||
// // return errors.New("please specify a name for your function.\nTry: fn init <FUNCTION_NAME>")
|
||||
// } else
|
||||
if a.Name == "" {
|
||||
// then use current directory for name
|
||||
a.Name = filepath.Base(pwd)
|
||||
} else if strings.Contains(a.Name, ":") {
|
||||
return errors.New("function name cannot contain a colon")
|
||||
}
|
||||
|
||||
if exists("Dockerfile") {
|
||||
fmt.Println("Dockerfile found. Let's use that to build...")
|
||||
return nil
|
||||
}
|
||||
var rt string
|
||||
if a.Runtime == "" {
|
||||
rt, err = detectRuntime(pwd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Runtime = rt
|
||||
fmt.Printf("Found %v, assuming %v runtime.\n", rt, rt)
|
||||
} else {
|
||||
fmt.Println("Runtime:", a.Runtime)
|
||||
}
|
||||
helper := langs.GetLangHelper(a.Runtime)
|
||||
if helper == nil {
|
||||
fmt.Printf("init does not support the %s runtime, you'll have to create your own Dockerfile for this function", a.Runtime)
|
||||
}
|
||||
|
||||
if a.Entrypoint == "" {
|
||||
if helper != nil {
|
||||
a.Entrypoint = helper.Entrypoint()
|
||||
}
|
||||
}
|
||||
if a.Cmd == "" {
|
||||
if helper != nil {
|
||||
a.Cmd = helper.Cmd()
|
||||
}
|
||||
}
|
||||
if a.Entrypoint == "" && a.Cmd == "" {
|
||||
return fmt.Errorf("could not detect entrypoint or cmd for %v, use --entrypoint and/or --cmd to set them explicitly", a.Runtime)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func detectRuntime(path string) (runtime string, err error) {
|
||||
for ext, runtime := range fileExtToRuntime {
|
||||
filenames := []string{
|
||||
filepath.Join(path, fmt.Sprintf("func%s", ext)),
|
||||
filepath.Join(path, fmt.Sprintf("Func%s", ext)),
|
||||
filepath.Join(path, fmt.Sprintf("src/main%s", ext)), // rust
|
||||
}
|
||||
for _, filename := range filenames {
|
||||
if exists(filename) {
|
||||
return runtime, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no supported files found to guess runtime, please set runtime explicitly with --runtime flag")
|
||||
}
|
||||
90
cli/install
90
cli/install
@@ -1,90 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# Install script to install fn
|
||||
|
||||
version="0.3.26"
|
||||
|
||||
command_exists() {
|
||||
command -v "$@" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
case "$(uname -m)" in
|
||||
*64)
|
||||
;;
|
||||
*)
|
||||
echo >&2 'Error: you are not using a 64bit platform.'
|
||||
echo >&2 'Functions CLI currently only supports 64bit platforms.'
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if command_exists fn ; then
|
||||
echo >&2 'Warning: "fn" command appears to already exist.'
|
||||
echo >&2 'If you are just upgrading your functions cli client, ignore this and wait a few seconds.'
|
||||
echo >&2 'You may press Ctrl+C now to abort this process.'
|
||||
( set -x; sleep 5 )
|
||||
fi
|
||||
|
||||
user="$(id -un 2>/dev/null || true)"
|
||||
|
||||
sh_c='sh -c'
|
||||
if [ "$user" != 'root' ]; then
|
||||
if command_exists sudo; then
|
||||
sh_c='sudo -E sh -c'
|
||||
elif command_exists su; then
|
||||
sh_c='su -c'
|
||||
else
|
||||
echo >&2 'Error: this installer needs the ability to run commands as root.'
|
||||
echo >&2 'We are unable to find either "sudo" or "su" available to make this happen.'
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
curl=''
|
||||
if command_exists curl; then
|
||||
curl='curl -sSL -o'
|
||||
elif command_exists wget; then
|
||||
curl='wget -qO'
|
||||
elif command_exists busybox && busybox --list-modules | grep -q wget; then
|
||||
curl='busybox wget -qO'
|
||||
else
|
||||
echo >&2 'Error: this installer needs the ability to run wget or curl.'
|
||||
echo >&2 'We are unable to find either "wget" or "curl" available to make this happen.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
url='https://github.com/fnproject/cli/releases/download'
|
||||
|
||||
# perform some very rudimentary platform detection
|
||||
case "$(uname)" in
|
||||
Linux)
|
||||
$sh_c "$curl /usr/local/bin/fn $url/$version/fn_linux"
|
||||
$sh_c "chmod +x /usr/local/bin/fn"
|
||||
fn --version
|
||||
exit 0
|
||||
;;
|
||||
Darwin)
|
||||
$sh_c "$curl /usr/local/bin/fn $url/$version/fn_mac"
|
||||
$sh_c "chmod +x /usr/local/bin/fn"
|
||||
fn --version
|
||||
exit 0
|
||||
;;
|
||||
WindowsNT)
|
||||
$sh_c "$curl $url/$version/fn.exe"
|
||||
# TODO how to make executable? chmod?
|
||||
fn.exe --version
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
cat >&2 <<'EOF'
|
||||
|
||||
Either your platform is not easily detectable or is not supported by this
|
||||
installer script (yet - PRs welcome! [fn/install]).
|
||||
Please visit the following URL for more detailed installation instructions:
|
||||
|
||||
https://github.com/fnproject/fn
|
||||
|
||||
EOF
|
||||
exit 1
|
||||
@@ -1,90 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# Install script to install fn
|
||||
|
||||
version="0.3.26"
|
||||
|
||||
command_exists() {
|
||||
command -v "$@" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
case "$(uname -m)" in
|
||||
*64)
|
||||
;;
|
||||
*)
|
||||
echo >&2 'Error: you are not using a 64bit platform.'
|
||||
echo >&2 'Functions CLI currently only supports 64bit platforms.'
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if command_exists fn ; then
|
||||
echo >&2 'Warning: "fn" command appears to already exist.'
|
||||
echo >&2 'If you are just upgrading your functions cli client, ignore this and wait a few seconds.'
|
||||
echo >&2 'You may press Ctrl+C now to abort this process.'
|
||||
( set -x; sleep 5 )
|
||||
fi
|
||||
|
||||
user="$(id -un 2>/dev/null || true)"
|
||||
|
||||
sh_c='sh -c'
|
||||
if [ "$user" != 'root' ]; then
|
||||
if command_exists sudo; then
|
||||
sh_c='sudo -E sh -c'
|
||||
elif command_exists su; then
|
||||
sh_c='su -c'
|
||||
else
|
||||
echo >&2 'Error: this installer needs the ability to run commands as root.'
|
||||
echo >&2 'We are unable to find either "sudo" or "su" available to make this happen.'
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
curl=''
|
||||
if command_exists curl; then
|
||||
curl='curl -sSL -o'
|
||||
elif command_exists wget; then
|
||||
curl='wget -qO'
|
||||
elif command_exists busybox && busybox --list-modules | grep -q wget; then
|
||||
curl='busybox wget -qO'
|
||||
else
|
||||
echo >&2 'Error: this installer needs the ability to run wget or curl.'
|
||||
echo >&2 'We are unable to find either "wget" or "curl" available to make this happen.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
url='https://github.com/fnproject/cli/releases/download'
|
||||
|
||||
# perform some very rudimentary platform detection
|
||||
case "$(uname)" in
|
||||
Linux)
|
||||
$sh_c "$curl /usr/local/bin/fn $url/$version/fn_linux"
|
||||
$sh_c "chmod +x /usr/local/bin/fn"
|
||||
fn --version
|
||||
exit 0
|
||||
;;
|
||||
Darwin)
|
||||
$sh_c "$curl /usr/local/bin/fn $url/$version/fn_mac"
|
||||
$sh_c "chmod +x /usr/local/bin/fn"
|
||||
fn --version
|
||||
exit 0
|
||||
;;
|
||||
WindowsNT)
|
||||
$sh_c "$curl $url/$version/fn.exe"
|
||||
# TODO how to make executable? chmod?
|
||||
fn.exe --version
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
cat >&2 <<'EOF'
|
||||
|
||||
Either your platform is not easily detectable or is not supported by this
|
||||
installer script (yet - PRs welcome! [fn/install]).
|
||||
Please visit the following URL for more detailed installation instructions:
|
||||
|
||||
https://github.com/fnproject/fn
|
||||
|
||||
EOF
|
||||
exit 1
|
||||
340
cli/lambda.go
340
cli/lambda.go
@@ -1,340 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
aws_lambda "github.com/aws/aws-sdk-go/service/lambda"
|
||||
"github.com/moby/moby/pkg/jsonmessage"
|
||||
"github.com/urfave/cli"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var runtimes = map[string]string{
|
||||
"nodejs4.3": "lambda-nodejs4.3",
|
||||
}
|
||||
|
||||
func lambda() cli.Command {
|
||||
var flags []cli.Flag
|
||||
|
||||
flags = append(flags, getFlags()...)
|
||||
|
||||
return cli.Command{
|
||||
Name: "lambda",
|
||||
Usage: "create and publish lambda functions",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "aws-import",
|
||||
Usage: `converts an existing Lambda function to an image, where the function code is downloaded to a directory in the current working directory that has the same name as the Lambda function.`,
|
||||
ArgsUsage: "<arn> <region> <image/name>",
|
||||
Action: awsImport,
|
||||
Flags: flags,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "payload",
|
||||
Usage: "Payload to pass to the Lambda function. This is usually a JSON object.",
|
||||
Value: "{}",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "version",
|
||||
Usage: "Version of the function to import.",
|
||||
Value: "$LATEST",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "download-only",
|
||||
Usage: "Only download the function into a directory. Will not create a Docker image.",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "config",
|
||||
Usage: "function configuration",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func transcribeEnvConfig(configs []string) map[string]string {
|
||||
c := make(map[string]string)
|
||||
for _, v := range configs {
|
||||
kv := strings.SplitN(v, "=", 2)
|
||||
if len(kv) == 1 {
|
||||
// TODO: Make sure it is compatible cross platform
|
||||
c[kv[0]] = fmt.Sprintf("$%s", kv[0])
|
||||
} else {
|
||||
c[kv[0]] = kv[1]
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func awsImport(c *cli.Context) error {
|
||||
args := c.Args()
|
||||
|
||||
version := c.String("version")
|
||||
downloadOnly := c.Bool("download-only")
|
||||
profile := c.String("profile")
|
||||
arn := args[0]
|
||||
region := args[1]
|
||||
image := args[2]
|
||||
|
||||
function, err := getFunction(profile, region, version, arn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
functionName := *function.Configuration.FunctionName
|
||||
|
||||
err = os.Mkdir(fmt.Sprintf("./%s", functionName), os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpFileName, err := downloadToFile(*function.Code.Location)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(tmpFileName)
|
||||
|
||||
if downloadOnly {
|
||||
// Since we are a command line program that will quit soon, it is OK to
|
||||
// let the OS clean `files` up.
|
||||
return err
|
||||
}
|
||||
|
||||
opts := createImageOptions{
|
||||
Name: functionName,
|
||||
Base: runtimes[(*function.Configuration.Runtime)],
|
||||
Package: "",
|
||||
Handler: *function.Configuration.Handler,
|
||||
OutputStream: newdockerJSONWriter(os.Stdout),
|
||||
RawJSONStream: true,
|
||||
Config: transcribeEnvConfig(c.StringSlice("config")),
|
||||
}
|
||||
|
||||
runtime := *function.Configuration.Runtime
|
||||
rh, ok := runtimeImportHandlers[runtime]
|
||||
if !ok {
|
||||
return fmt.Errorf("unsupported runtime %v", runtime)
|
||||
}
|
||||
|
||||
_, err = rh(functionName, tmpFileName, &opts)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if image != "" {
|
||||
opts.Name = image
|
||||
}
|
||||
|
||||
fmt.Print("Creating func.yaml ... ")
|
||||
if err := createFunctionYaml(opts, functionName); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("OK")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
runtimeImportHandlers = map[string]func(functionName, tmpFileName string, opts *createImageOptions) ([]fileLike, error){
|
||||
"nodejs4.3": basicImportHandler,
|
||||
"python2.7": basicImportHandler,
|
||||
"java8": func(functionName, tmpFileName string, opts *createImageOptions) ([]fileLike, error) {
|
||||
fmt.Println("Found Java Lambda function. Going to assume code is a single JAR file.")
|
||||
path := filepath.Join(functionName, "function.jar")
|
||||
if err := os.Rename(tmpFileName, path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fd, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
files := []fileLike{fd}
|
||||
opts.Package = filepath.Base(files[0].(*os.File).Name())
|
||||
return files, nil
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func basicImportHandler(functionName, tmpFileName string, opts *createImageOptions) ([]fileLike, error) {
|
||||
return unzipAndGetTopLevelFiles(functionName, tmpFileName)
|
||||
}
|
||||
|
||||
func createFunctionYaml(opts createImageOptions, functionName string) error {
|
||||
strs := strings.Split(opts.Name, "/")
|
||||
path := fmt.Sprintf("/%s", strs[1])
|
||||
|
||||
funcDesc := &funcfile{
|
||||
Name: opts.Name,
|
||||
Version: "0.0.1",
|
||||
Runtime: opts.Base,
|
||||
Cmd: opts.Handler,
|
||||
}
|
||||
funcDesc.Config = opts.Config
|
||||
funcDesc.Path = path
|
||||
|
||||
out, err := yaml.Marshal(funcDesc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filepath.Join(functionName, "func.yaml"), out, 0644)
|
||||
}
|
||||
|
||||
type createImageOptions struct {
|
||||
Name string
|
||||
Base string
|
||||
Package string // Used for Java, empty string for others.
|
||||
Handler string
|
||||
OutputStream io.Writer
|
||||
RawJSONStream bool
|
||||
Config map[string]string
|
||||
}
|
||||
|
||||
type fileLike interface {
|
||||
io.Reader
|
||||
Stat() (os.FileInfo, error)
|
||||
}
|
||||
|
||||
var errNoFiles = errors.New("No files to add to image")
|
||||
|
||||
type dockerJSONWriter struct {
|
||||
under io.Writer
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func newdockerJSONWriter(under io.Writer) *dockerJSONWriter {
|
||||
r, w := io.Pipe()
|
||||
go func() {
|
||||
err := jsonmessage.DisplayJSONMessagesStream(r, under, 1, true, nil)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
return &dockerJSONWriter{under, w}
|
||||
}
|
||||
|
||||
func (djw *dockerJSONWriter) Write(p []byte) (int, error) {
|
||||
return djw.w.Write(p)
|
||||
}
|
||||
|
||||
func downloadToFile(url string) (string, error) {
|
||||
downloadResp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer downloadResp.Body.Close()
|
||||
|
||||
// zip reader needs ReaderAt, hence the indirection.
|
||||
tmpFile, err := ioutil.TempFile("", "lambda-function-")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if _, err := io.Copy(tmpFile, downloadResp.Body); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := tmpFile.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tmpFile.Name(), nil
|
||||
}
|
||||
|
||||
func unzipAndGetTopLevelFiles(dst, src string) (files []fileLike, topErr error) {
|
||||
files = make([]fileLike, 0)
|
||||
|
||||
zipReader, err := zip.OpenReader(src)
|
||||
if err != nil {
|
||||
return files, err
|
||||
}
|
||||
defer zipReader.Close()
|
||||
|
||||
var fd *os.File
|
||||
for _, f := range zipReader.File {
|
||||
path := filepath.Join(dst, f.Name)
|
||||
fmt.Printf("Extracting '%s' to '%s'\n", f.Name, path)
|
||||
if f.FileInfo().IsDir() {
|
||||
if err := os.Mkdir(path, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Only top-level dirs go into the list since that is what CreateImage expects.
|
||||
if filepath.Dir(f.Name) == filepath.Base(f.Name) {
|
||||
fd, topErr = os.Open(path)
|
||||
if topErr != nil {
|
||||
break
|
||||
}
|
||||
files = append(files, fd)
|
||||
}
|
||||
} else {
|
||||
// We do not close fd here since we may want to use it to dockerize.
|
||||
fd, topErr = os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
|
||||
if topErr != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var zipFd io.ReadCloser
|
||||
zipFd, topErr = f.Open()
|
||||
if topErr != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if _, topErr = io.Copy(fd, zipFd); topErr != nil {
|
||||
// OK to skip closing fd here.
|
||||
break
|
||||
}
|
||||
|
||||
if err := zipFd.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Only top-level files go into the list since that is what CreateImage expects.
|
||||
if filepath.Dir(f.Name) == "." {
|
||||
if _, topErr = fd.Seek(0, 0); topErr != nil {
|
||||
break
|
||||
}
|
||||
|
||||
files = append(files, fd)
|
||||
} else {
|
||||
if err := fd.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getFunction(awsProfile, awsRegion, version, arn string) (*aws_lambda.GetFunctionOutput, error) {
|
||||
creds := credentials.NewChainCredentials([]credentials.Provider{
|
||||
&credentials.EnvProvider{},
|
||||
&credentials.SharedCredentialsProvider{
|
||||
Filename: "", // Look in default location.
|
||||
Profile: awsProfile,
|
||||
},
|
||||
})
|
||||
|
||||
conf := aws.NewConfig().WithCredentials(creds).WithCredentialsChainVerboseErrors(true).WithRegion(awsRegion)
|
||||
sess := session.New(conf)
|
||||
conn := aws_lambda.New(sess)
|
||||
resp, err := conn.GetFunction(&aws_lambda.GetFunctionInput{
|
||||
FunctionName: aws.String(arn),
|
||||
Qualifier: aws.String(version),
|
||||
})
|
||||
|
||||
return resp, err
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
FROM node:4-alpine
|
||||
|
||||
WORKDIR /function
|
||||
|
||||
# Install ImageMagick and AWS SDK as provided by Lambda.
|
||||
RUN apk update && apk --no-cache add imagemagick
|
||||
RUN npm install aws-sdk@2.2.32 imagemagick && npm cache clear
|
||||
|
||||
# cli should forbid this name
|
||||
ADD bootstrap.js /function/lambda-bootstrap.js
|
||||
|
||||
# Run the handler, with a payload in the future.
|
||||
ENTRYPOINT ["node", "./lambda-bootstrap"]
|
||||
403
cli/lambda/node-4/bootstrap.js
vendored
403
cli/lambda/node-4/bootstrap.js
vendored
@@ -1,403 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var net = require('net');
|
||||
// var http = require('http');
|
||||
var http_common = require('_http_common');
|
||||
var events = require('events');
|
||||
var HTTPParser = process.binding('http_parser').HTTPParser;
|
||||
|
||||
var oldlog = console.log
|
||||
console.log = console.error
|
||||
|
||||
// Some notes on the semantics of the succeed(), fail() and done() methods.
|
||||
// Tests are the source of truth!
|
||||
// First call wins in terms of deciding the result of the function. BUT,
|
||||
// subsequent calls also log. Further, code execution does not stop, even where
|
||||
// for done(), the docs say that the "function terminates". It seems though
|
||||
// that further cycles of the event loop do not run. For example:
|
||||
// index.handler = function(event, context) {
|
||||
// context.fail("FAIL")
|
||||
// process.nextTick(function() {
|
||||
// console.log("This does not get logged")
|
||||
// })
|
||||
// console.log("This does get logged")
|
||||
// }
|
||||
// on the other hand:
|
||||
// index.handler = function(event, context) {
|
||||
// process.nextTick(function() {
|
||||
// console.log("This also gets logged")
|
||||
// context.fail("FAIL")
|
||||
// })
|
||||
// console.log("This does get logged")
|
||||
// }
|
||||
//
|
||||
// The same is true for context.succeed() and done() captures the semantics of
|
||||
// both. It seems this is implemented simply by having process.nextTick() cause
|
||||
// process.exit() or similar, because the following:
|
||||
// exports.handler = function(event, context) {
|
||||
// process.nextTick(function() {console.log("This gets logged")})
|
||||
// process.nextTick(function() {console.log("This also gets logged")})
|
||||
// context.succeed("END")
|
||||
// process.nextTick(function() {console.log("This does not get logged")})
|
||||
// };
|
||||
//
|
||||
// So the context object needs to have some sort of hidden boolean that is only
|
||||
// flipped once, by the first call, and dictates the behavior on the next tick.
|
||||
//
|
||||
// In addition, the response behaviour depends on the invocation type. If we
|
||||
// are to only support the async type, succeed() must return a 202 response
|
||||
// code, not sure how to do this.
|
||||
//
|
||||
// Only the first 256kb, followed by a truncation message, should be logged.
|
||||
//
|
||||
// Also, the error log is always in a json literal
|
||||
// { "errorMessage": "<message>" }
|
||||
var Context = function() {
|
||||
var concluded = false;
|
||||
|
||||
var contextSelf = this;
|
||||
|
||||
// The succeed, fail and done functions are public, but access a private
|
||||
// member (concluded). Hence this ugly nested definition.
|
||||
this.succeed = function(result) {
|
||||
if (concluded) {
|
||||
return
|
||||
}
|
||||
|
||||
// We have to process the result before we can conclude, because otherwise
|
||||
// we have to fail. This means NO EARLY RETURNS from this function without
|
||||
// review!
|
||||
if (result === undefined) {
|
||||
result = null
|
||||
}
|
||||
|
||||
var failed = false;
|
||||
try {
|
||||
// Output result to log
|
||||
oldlog(JSON.stringify(result));
|
||||
} catch(e) {
|
||||
// Set X-Amz-Function-Error: Unhandled header
|
||||
console.log("Unable to stringify body as json: " + e);
|
||||
failed = true;
|
||||
}
|
||||
|
||||
// FIXME(nikhil): Return 202 or 200 based on invocation type and set response
|
||||
// to result. Should probably be handled externally by the runner/swapi.
|
||||
|
||||
// OK, everything good.
|
||||
concluded = true;
|
||||
process.nextTick(function() { process.exit(failed ? 1 : 0) })
|
||||
}
|
||||
|
||||
this.fail = function(error) {
|
||||
if (concluded) {
|
||||
return
|
||||
}
|
||||
|
||||
concluded = true
|
||||
process.nextTick(function() { process.exit(1) })
|
||||
|
||||
if (error === undefined) {
|
||||
error = null
|
||||
}
|
||||
|
||||
// FIXME(nikhil): Truncated log of error, plus non-truncated response body
|
||||
var errstr = "fail() called with argument but a problem was encountered while converting it to a to string";
|
||||
|
||||
// The semantics of fail() are weird. If the error is something that can be
|
||||
// converted to a string, the log output wraps the string in a JSON literal
|
||||
// with key "errorMessage". If toString() fails, then the output is only
|
||||
// the error string.
|
||||
try {
|
||||
if (error === null) {
|
||||
errstr = null
|
||||
} else {
|
||||
errstr = error.toString()
|
||||
}
|
||||
oldlog(JSON.stringify({"errorMessage": errstr }))
|
||||
} catch(e) {
|
||||
// Set X-Amz-Function-Error: Unhandled header
|
||||
oldlog(errstr)
|
||||
}
|
||||
}
|
||||
|
||||
this.done = function() {
|
||||
var error = arguments[0];
|
||||
var result = arguments[1];
|
||||
if (error) {
|
||||
contextSelf.fail(error)
|
||||
} else {
|
||||
contextSelf.succeed(result)
|
||||
}
|
||||
}
|
||||
|
||||
var plannedEnd = Date.now() + (getTimeoutInSeconds() * 1000);
|
||||
this.getRemainingTimeInMillis = function() {
|
||||
return Math.max(plannedEnd - Date.now(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
function getTimeoutInSeconds() {
|
||||
var t = parseInt(getEnv("TASK_TIMEOUT"));
|
||||
if (Number.isNaN(t)) {
|
||||
return 3600;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
var getEnv = function(name) {
|
||||
return process.env[name] || "";
|
||||
}
|
||||
|
||||
var makeCtx = function() {
|
||||
var fnname = getEnv("AWS_LAMBDA_FUNCTION_NAME");
|
||||
// FIXME(nikhil): Generate UUID.
|
||||
var taskID = getEnv("TASK_ID");
|
||||
|
||||
var mem = getEnv("TASK_MAXRAM").toLowerCase();
|
||||
var bytes = 300 * 1024 * 1024;
|
||||
|
||||
var scale = { 'b': 1, 'k': 1024, 'm': 1024*1024, 'g': 1024*1024*1024 };
|
||||
// We don't bother validating too much, if the last character is not a number
|
||||
// and not in the scale table we just return a default value.
|
||||
// We use slice instead of indexing so that we always get an empty string,
|
||||
// instead of undefined.
|
||||
if (mem.slice(-1).match(/[0-9]/)) {
|
||||
var a = parseInt(mem);
|
||||
if (!Number.isNaN(a)) {
|
||||
bytes = a;
|
||||
}
|
||||
} else {
|
||||
var rem = parseInt(mem.slice(0, -1));
|
||||
if (!Number.isNaN(rem)) {
|
||||
var multiplier = scale[mem.slice(-1)];
|
||||
if (multiplier) {
|
||||
bytes = rem * multiplier
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var memoryMB = bytes / (1024 * 1024);
|
||||
|
||||
var ctx = new Context();
|
||||
Object.defineProperties(ctx, {
|
||||
"functionName": {
|
||||
value: fnname,
|
||||
enumerable: true,
|
||||
},
|
||||
"functionVersion": {
|
||||
value: "$LATEST",
|
||||
enumerable: true,
|
||||
},
|
||||
"invokedFunctionArn": {
|
||||
// FIXME(nikhil): Should be filled in.
|
||||
value: "",
|
||||
enumerable: true,
|
||||
},
|
||||
"memoryLimitInMB": {
|
||||
// Sigh, yes it is a string.
|
||||
value: ""+memoryMB,
|
||||
enumerable: true,
|
||||
},
|
||||
"awsRequestId": {
|
||||
value: taskID,
|
||||
enumerable: true,
|
||||
},
|
||||
"logGroupName": {
|
||||
// FIXME(nikhil): Should be filled in.
|
||||
value: "",
|
||||
enumerable: true,
|
||||
},
|
||||
"logStreamName": {
|
||||
// FIXME(nikhil): Should be filled in.
|
||||
value: "",
|
||||
enumerable: true,
|
||||
},
|
||||
"identity": {
|
||||
// FIXME(nikhil): Should be filled in.
|
||||
value: null,
|
||||
enumerable: true,
|
||||
},
|
||||
"clientContext": {
|
||||
// FIXME(nikhil): Should be filled in.
|
||||
value: null,
|
||||
enumerable: true,
|
||||
},
|
||||
});
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
var setEnvFromHeader = function () {
|
||||
var headerPrefix = "CONFIG_";
|
||||
var newEnvVars = {};
|
||||
for (var key in process.env) {
|
||||
if (key.indexOf(headerPrefix) == 0) {
|
||||
newEnvVars[key.slice(headerPrefix.length)] = process.env[key];
|
||||
}
|
||||
}
|
||||
|
||||
for (var key in newEnvVars) {
|
||||
process.env[key] = newEnvVars[key];
|
||||
}
|
||||
}
|
||||
|
||||
// for http hot functions
|
||||
function freeParser(parser){
|
||||
if (parser) {
|
||||
parser.onIncoming = null;
|
||||
parser.socket = null;
|
||||
http_common.parsers.free(parser);
|
||||
parser = null;
|
||||
}
|
||||
};
|
||||
|
||||
// parses http requests
|
||||
function parse(socket){
|
||||
var emitter = new events.EventEmitter();
|
||||
var parser = http_common.parsers.alloc();
|
||||
|
||||
parser.reinitialize(HTTPParser.REQUEST);
|
||||
parser.socket = socket;
|
||||
parser.maxHeaderPairs = 2000;
|
||||
|
||||
parser.onIncoming = function(req){
|
||||
emitter.emit('request', req);
|
||||
};
|
||||
|
||||
socket.on('data', function(buffer){
|
||||
var ret = parser.execute(buffer, 0, buffer.length);
|
||||
if(ret instanceof Error){
|
||||
emitter.emit('error');
|
||||
freeParser(parser);
|
||||
}
|
||||
});
|
||||
|
||||
socket.once('close', function(){
|
||||
freeParser(parser);
|
||||
});
|
||||
|
||||
return emitter;
|
||||
};
|
||||
|
||||
function run() {
|
||||
|
||||
setEnvFromHeader();
|
||||
var path = process.env["PAYLOAD_FILE"]; // if we allow a mounted file, this is used. Could safely be removed.
|
||||
var stream = process.stdin;
|
||||
if (path) {
|
||||
try {
|
||||
stream = fs.createReadStream(path);
|
||||
} catch(e) {
|
||||
console.error("bootstrap: Error opening payload file", e)
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// First, check format (ie: hot functions)
|
||||
var format = process.env["FORMAT"];
|
||||
if (format == "http"){
|
||||
// var parser = httpSocketSetup(stream)
|
||||
// init parser
|
||||
var parser = parse(stream);
|
||||
let i = 0;
|
||||
parser.on('request', function(req){
|
||||
// Got parsed HTTP object
|
||||
// console.error("REQUEST", req)
|
||||
i++;
|
||||
console.error("REQUEST:", i)
|
||||
handleRequest(req);
|
||||
});
|
||||
|
||||
parser.on('error', function(e){
|
||||
// Not HTTP data
|
||||
console.error("INVALID HTTP DATA!", e)
|
||||
});
|
||||
|
||||
} else {
|
||||
// default
|
||||
handleRequest(stream);
|
||||
}
|
||||
}
|
||||
|
||||
function handleRequest(stream) {
|
||||
var input = "";
|
||||
stream.setEncoding('utf8');
|
||||
stream.on('data', function(chunk) {
|
||||
input += chunk;
|
||||
});
|
||||
stream.on('error', function(err) {
|
||||
console.error("bootstrap: Error reading payload stream", err);
|
||||
process.exit(1);
|
||||
});
|
||||
stream.on('end', function() {
|
||||
var payload = {}
|
||||
try {
|
||||
if (input.length > 0) {
|
||||
payload = JSON.parse(input);
|
||||
}
|
||||
} catch(e) {
|
||||
console.error("bootstrap: Error parsing JSON", e);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
handlePayload(payload)
|
||||
})
|
||||
}
|
||||
|
||||
function handlePayload(payload) {
|
||||
if (process.argv.length > 2) {
|
||||
var handler = process.argv[2];
|
||||
var parts = handler.split('.');
|
||||
// FIXME(nikhil): Error checking.
|
||||
var script = parts[0];
|
||||
var entry = parts[1];
|
||||
var started = false;
|
||||
try {
|
||||
var mod = require('./'+script);
|
||||
var func = mod[entry];
|
||||
if (func === undefined) {
|
||||
oldlog("Handler '" + entry + "' missing on module '" + script + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof func !== 'function') {
|
||||
throw "TypeError: " + (typeof func) + " is not a function";
|
||||
}
|
||||
started = true;
|
||||
var cback
|
||||
// RUN THE FUNCTION:
|
||||
mod[entry](payload, makeCtx(), functionCallback)
|
||||
} catch(e) {
|
||||
if (typeof e === 'string') {
|
||||
oldlog(e)
|
||||
} else {
|
||||
oldlog(e.message)
|
||||
}
|
||||
if (!started) {
|
||||
oldlog("Process exited before completing request\n")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error("bootstrap: No script specified")
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function functionCallback(err, result) {
|
||||
if (err != null) {
|
||||
// then user returned error and we should respond with error
|
||||
// http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-mode-exceptions.html
|
||||
oldlog(JSON.stringify({"errorMessage": errstr }))
|
||||
return
|
||||
}
|
||||
if (result != null) {
|
||||
oldlog(JSON.stringify(result))
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
||||
@@ -1,3 +0,0 @@
|
||||
set -ex
|
||||
|
||||
docker build --build-arg HTTP_PROXY -t funcy/lambda:node-4 .
|
||||
@@ -1,5 +0,0 @@
|
||||
set -ex
|
||||
|
||||
./build.sh
|
||||
|
||||
docker push funcy/lambda:node-4
|
||||
@@ -1,13 +0,0 @@
|
||||
FROM node:6-alpine
|
||||
|
||||
WORKDIR /function
|
||||
|
||||
# Install ImageMagick and AWS SDK as provided by Lambda.
|
||||
RUN apk update && apk --no-cache add imagemagick
|
||||
RUN npm install aws-sdk@2.2.32 imagemagick && npm cache clear
|
||||
|
||||
# cli should forbid this name
|
||||
ADD bootstrap.js /function/lambda-bootstrap.js
|
||||
|
||||
# Run the handler, with a payload in the future.
|
||||
ENTRYPOINT ["node", "./lambda-bootstrap"]
|
||||
@@ -1,3 +0,0 @@
|
||||
set -ex
|
||||
|
||||
docker build --build-arg HTTP_PROXY -t funcy/lambda:node-6 .
|
||||
@@ -1,5 +0,0 @@
|
||||
set -ex
|
||||
|
||||
./build.sh
|
||||
|
||||
docker push funcy/lambda:node-6
|
||||
@@ -1,9 +0,0 @@
|
||||
set -ex
|
||||
|
||||
cd node-4
|
||||
./release.sh
|
||||
cd ..
|
||||
|
||||
cd node-6
|
||||
./release.sh
|
||||
cd ..
|
||||
@@ -1,92 +0,0 @@
|
||||
package langs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBoilerplateExists = errors.New("Function boilerplate already exists")
|
||||
)
|
||||
|
||||
// GetLangHelper returns a LangHelper for the passed in language
|
||||
func GetLangHelper(lang string) LangHelper {
|
||||
switch lang {
|
||||
case "go":
|
||||
return &GoLangHelper{}
|
||||
case "node":
|
||||
return &NodeLangHelper{}
|
||||
case "ruby":
|
||||
return &RubyLangHelper{}
|
||||
case "python":
|
||||
return &PythonLangHelper{}
|
||||
case "php":
|
||||
return &PhpLangHelper{}
|
||||
case "rust":
|
||||
return &RustLangHelper{}
|
||||
case "dotnet":
|
||||
return &DotNetLangHelper{}
|
||||
case "lambda-nodejs4.3", "lambda-node-4":
|
||||
return &LambdaNodeHelper{}
|
||||
case "java":
|
||||
return &JavaLangHelper{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type LangHelper interface {
|
||||
// BuildFromImage is the base image to build off, typically funcy/LANG:dev
|
||||
BuildFromImage() string
|
||||
// RunFromImage is the base image to use for deployment (usually smaller than the build images)
|
||||
RunFromImage() string
|
||||
// If set to false, it will use a single Docker build step, rather than multi-stage
|
||||
IsMultiStage() bool
|
||||
// Dockerfile build lines for building dependencies or anything else language specific
|
||||
DockerfileBuildCmds() []string
|
||||
// DockerfileCopyCmds will run in second/final stage of multi-stage build to copy artifacts form the build stage
|
||||
DockerfileCopyCmds() []string
|
||||
// Entrypoint sets the Docker Entrypoint. One of Entrypoint or Cmd is required.
|
||||
Entrypoint() string
|
||||
// Cmd sets the Docker command. One of Entrypoint or Cmd is required.
|
||||
Cmd() string
|
||||
HasPreBuild() bool
|
||||
PreBuild() error
|
||||
AfterBuild() error
|
||||
// HasBoilerplate indicates whether a language has support for generating function boilerplate.
|
||||
HasBoilerplate() bool
|
||||
// GenerateBoilerplate generates basic function boilerplate. Returns ErrBoilerplateExists if the function file
|
||||
// already exists.
|
||||
GenerateBoilerplate() error
|
||||
}
|
||||
|
||||
// BaseHelper is empty implementation of LangHelper for embedding in implementations.
|
||||
type BaseHelper struct {
|
||||
}
|
||||
|
||||
func (h *BaseHelper) BuildFromImage() string { return "" }
|
||||
func (h *BaseHelper) RunFromImage() string { return h.BuildFromImage() }
|
||||
func (h *BaseHelper) IsMultiStage() bool { return true }
|
||||
func (h *BaseHelper) DockerfileBuildCmds() []string { return []string{} }
|
||||
func (h *BaseHelper) DockerfileCopyCmds() []string { return []string{} }
|
||||
func (h *BaseHelper) Entrypoint() string { return "" }
|
||||
func (h *BaseHelper) Cmd() string { return "" }
|
||||
func (h *BaseHelper) HasPreBuild() bool { return false }
|
||||
func (h *BaseHelper) PreBuild() error { return nil }
|
||||
func (h *BaseHelper) AfterBuild() error { return nil }
|
||||
func (h *BaseHelper) HasBoilerplate() bool { return false }
|
||||
func (h *BaseHelper) GenerateBoilerplate() error { return nil }
|
||||
|
||||
// exists checks if a file exists
|
||||
func exists(name string) bool {
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func dockerBuildError(err error) error {
|
||||
return fmt.Errorf("error running docker build: %v", err)
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package langs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
type DotNetLangHelper struct {
|
||||
BaseHelper
|
||||
}
|
||||
|
||||
func (lh *DotNetLangHelper) BuildFromImage() string {
|
||||
return "microsoft/dotnet:1.0.1-sdk-projectjson"
|
||||
}
|
||||
func (lh *DotNetLangHelper) RunFromImage() string {
|
||||
return "microsoft/dotnet:runtime"
|
||||
}
|
||||
|
||||
func (lh *DotNetLangHelper) Entrypoint() string {
|
||||
return "dotnet dotnet.dll"
|
||||
}
|
||||
|
||||
func (lh *DotNetLangHelper) HasPreBuild() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// PreBuild for Go builds the binary so the final image can be as small as possible
|
||||
func (lh *DotNetLangHelper) PreBuild() error {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command(
|
||||
"docker", "run",
|
||||
"--rm", "-v",
|
||||
wd+":/dotnet", "-w", "/dotnet", "microsoft/dotnet:1.0.1-sdk-projectjson",
|
||||
"/bin/sh", "-c", "dotnet restore && dotnet publish -c release -b /tmp -o .",
|
||||
)
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
return dockerBuildError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lh *DotNetLangHelper) AfterBuild() error {
|
||||
return nil
|
||||
}
|
||||
127
cli/langs/go.go
127
cli/langs/go.go
@@ -1,127 +0,0 @@
|
||||
package langs
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type GoLangHelper struct {
|
||||
BaseHelper
|
||||
}
|
||||
|
||||
func (lh *GoLangHelper) BuildFromImage() string {
|
||||
return "funcy/go:dev"
|
||||
}
|
||||
|
||||
func (lh *GoLangHelper) RunFromImage() string {
|
||||
return "funcy/go"
|
||||
}
|
||||
|
||||
func (h *GoLangHelper) DockerfileBuildCmds() []string {
|
||||
r := []string{}
|
||||
// more info on Go multi-stage builds: https://medium.com/travis-on-docker/multi-stage-docker-builds-for-creating-tiny-go-images-e0e1867efe5a
|
||||
// For now we assume that dependencies are vendored already, but we could vendor them
|
||||
// inside the container. Maybe we should check for /vendor dir and if it doesn't exist,
|
||||
// either run `dep init` if no Gopkg.toml/lock found or `dep ensure` if it's there.
|
||||
r = append(r, "ADD . /go/src/func/")
|
||||
// if exists("Gopkg.toml") {
|
||||
// r = append(r,
|
||||
// "RUN go get -u github.com/golang/dep/cmd/dep",
|
||||
// "RUN cd /src && dep ensure",
|
||||
// )
|
||||
// }
|
||||
r = append(r, "RUN cd /go/src/func/ && go build -o func")
|
||||
return r
|
||||
}
|
||||
|
||||
func (h *GoLangHelper) DockerfileCopyCmds() []string {
|
||||
return []string{
|
||||
"COPY --from=build-stage /go/src/func/func /function/",
|
||||
}
|
||||
}
|
||||
|
||||
func (lh *GoLangHelper) Entrypoint() string {
|
||||
return "./func"
|
||||
}
|
||||
|
||||
// HasPreBuild returns whether the Java runtime has boilerplate that can be generated.
|
||||
func (lh *GoLangHelper) HasBoilerplate() bool { return true }
|
||||
|
||||
// GenerateBoilerplate will generate function boilerplate for a Java runtime. The default boilerplate is for a Maven
|
||||
// project.
|
||||
func (lh *GoLangHelper) GenerateBoilerplate() error {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
codeFile := filepath.Join(wd, "func.go")
|
||||
if exists(codeFile) {
|
||||
return ErrBoilerplateExists
|
||||
}
|
||||
testFile := filepath.Join(wd, "test.json")
|
||||
if exists(testFile) {
|
||||
return ErrBoilerplateExists
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(codeFile, []byte(helloGoSrcBoilerplate), os.FileMode(0644)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(testFile, []byte(testBoilerPlate), os.FileMode(0644)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
helloGoSrcBoilerplate = `package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Person struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
p := &Person{Name: "World"}
|
||||
json.NewDecoder(os.Stdin).Decode(p)
|
||||
mapD := map[string]string{"message": fmt.Sprintf("Hello %s", p.Name)}
|
||||
mapB, _ := json.Marshal(mapD)
|
||||
fmt.Println(string(mapB))
|
||||
}
|
||||
`
|
||||
|
||||
// Could use same test for most langs
|
||||
testBoilerPlate = `{
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"body": {
|
||||
"name": "Johnny"
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"body": {
|
||||
"message": "Hello Johnny"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"body": ""
|
||||
},
|
||||
"output": {
|
||||
"body": {
|
||||
"message": "Hello World"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
)
|
||||
@@ -1,238 +0,0 @@
|
||||
package langs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// JavaLangHelper provides a set of helper methods for the lifecycle of Java Maven projects
|
||||
type JavaLangHelper struct {
|
||||
BaseHelper
|
||||
}
|
||||
|
||||
// BuildFromImage returns the Docker image used to compile the Maven function project
|
||||
func (lh *JavaLangHelper) BuildFromImage() string { return "maven:3.5-jdk-8-alpine" }
|
||||
|
||||
// RunFromImage returns the Docker image used to run the Java function.
|
||||
func (lh *JavaLangHelper) RunFromImage() string {
|
||||
return "fnproject/fn-java-fdk:latest"
|
||||
}
|
||||
|
||||
// HasPreBuild returns whether the Java runtime has boilerplate that can be generated.
|
||||
func (lh *JavaLangHelper) HasBoilerplate() bool { return true }
|
||||
|
||||
// GenerateBoilerplate will generate function boilerplate for a Java runtime. The default boilerplate is for a Maven
|
||||
// project.
|
||||
func (lh *JavaLangHelper) GenerateBoilerplate() error {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pathToPomFile := filepath.Join(wd, "pom.xml")
|
||||
if exists(pathToPomFile) {
|
||||
return ErrBoilerplateExists
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(pathToPomFile, []byte(pomFile), os.FileMode(0644)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mkDirAndWriteFile := func(dir, filename, content string) error {
|
||||
fullPath := filepath.Join(wd, dir)
|
||||
if err = os.MkdirAll(fullPath, os.FileMode(0755)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fullFilePath := filepath.Join(fullPath, filename)
|
||||
return ioutil.WriteFile(fullFilePath, []byte(content), os.FileMode(0644))
|
||||
}
|
||||
|
||||
err = mkDirAndWriteFile("src/main/java/com/example/fn", "HelloFunction.java", helloJavaSrcBoilerplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mkDirAndWriteFile("src/test/java/com/example/fn", "HelloFunctionTest.java", helloJavaTestBoilerplate)
|
||||
}
|
||||
|
||||
// Entrypoint returns the Java runtime Docker entrypoint that will be executed when the function is executed.
|
||||
func (lh *JavaLangHelper) Cmd() string {
|
||||
return "com.example.fn.HelloFunction::handleRequest"
|
||||
}
|
||||
|
||||
// DockerfileCopyCmds returns the Docker COPY command to copy the compiled Java function jar and dependencies.
|
||||
func (lh *JavaLangHelper) DockerfileCopyCmds() []string {
|
||||
return []string{
|
||||
"COPY --from=build-stage /function/target/*.jar /function/app/",
|
||||
}
|
||||
}
|
||||
|
||||
// DockerfileBuildCmds returns the build stage steps to compile the Maven function project.
|
||||
func (lh *JavaLangHelper) DockerfileBuildCmds() []string {
|
||||
return []string{
|
||||
fmt.Sprintf("ENV MAVEN_OPTS %s", mavenOpts()),
|
||||
"ADD pom.xml /function/pom.xml",
|
||||
"RUN [\"mvn\", \"package\", \"dependency:copy-dependencies\", \"-DincludeScope=runtime\", " +
|
||||
"\"-DskipTests=true\", \"-Dmdep.prependGroupId=true\", \"-DoutputDirectory=target\", \"--fail-never\"]",
|
||||
"ADD src /function/src",
|
||||
"RUN [\"mvn\", \"package\"]",
|
||||
}
|
||||
}
|
||||
|
||||
// HasPreBuild returns whether the Java Maven runtime has a pre-build step.
|
||||
func (lh *JavaLangHelper) HasPreBuild() bool { return true }
|
||||
|
||||
// PreBuild ensures that the expected the function is based is a maven project.
|
||||
func (lh *JavaLangHelper) PreBuild() error {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists(filepath.Join(wd, "pom.xml")) {
|
||||
return errors.New("Could not find pom.xml - are you sure this is a Maven project?")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mavenOpts() string {
|
||||
var opts bytes.Buffer
|
||||
|
||||
if parsedURL, err := url.Parse(os.Getenv("http_proxy")); err == nil {
|
||||
opts.WriteString(fmt.Sprintf("-Dhttp.proxyHost=%s ", parsedURL.Hostname()))
|
||||
opts.WriteString(fmt.Sprintf("-Dhttp.proxyPort=%s ", parsedURL.Port()))
|
||||
}
|
||||
|
||||
if parsedURL, err := url.Parse(os.Getenv("https_proxy")); err == nil {
|
||||
opts.WriteString(fmt.Sprintf("-Dhttps.proxyHost=%s ", parsedURL.Hostname()))
|
||||
opts.WriteString(fmt.Sprintf("-Dhttps.proxyPort=%s ", parsedURL.Port()))
|
||||
}
|
||||
|
||||
nonProxyHost := os.Getenv("no_proxy")
|
||||
opts.WriteString(fmt.Sprintf("-Dhttp.nonProxyHosts=%s ", strings.Replace(nonProxyHost, ",", "|", -1)))
|
||||
|
||||
opts.WriteString("-Dmaven.repo.local=/usr/share/maven/ref/repository")
|
||||
|
||||
return opts.String()
|
||||
}
|
||||
|
||||
/* TODO temporarily generate maven project boilerplate from hardcoded values.
|
||||
Will eventually move to using a maven archetype.
|
||||
*/
|
||||
|
||||
const (
|
||||
pomFile = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<groupId>com.example.fn</groupId>
|
||||
<artifactId>hello</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>fn-release-repo</id>
|
||||
<url>https://swiftobjectstorage.us-phoenix-1.oraclecloud.com/v1/opc0002/mvnrepo/releases</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>fn-snapshot-repo</id>
|
||||
<url>https://swiftobjectstorage.us-phoenix-1.oraclecloud.com/v1/opc0002/mvnrepo/snapshots</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
<updatePolicy>always</updatePolicy>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fnproject.fn</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fnproject.fn</groupId>
|
||||
<artifactId>testing</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.3</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
`
|
||||
|
||||
helloJavaSrcBoilerplate = `package com.example.fn;
|
||||
|
||||
public class HelloFunction {
|
||||
|
||||
public String handleRequest(String input) {
|
||||
String name = (input == null || input.isEmpty()) ? "world" : input;
|
||||
|
||||
return "Hello, " + name + "!";
|
||||
}
|
||||
|
||||
}`
|
||||
|
||||
helloJavaTestBoilerplate = `package com.example.fn;
|
||||
|
||||
import com.fnproject.fn.testing.*;
|
||||
import org.junit.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class HelloFunctionTest {
|
||||
|
||||
@Rule
|
||||
public final FnTestingRule testing = FnTestingRule.createDefault();
|
||||
|
||||
@Test
|
||||
public void shouldReturnGreeting() {
|
||||
testing.givenEvent().enqueue();
|
||||
testing.thenRun(HelloFunction.class, "handleRequest");
|
||||
|
||||
FnResult result = testing.getOnlyResult();
|
||||
assertEquals("Hello, world!", result.getBodyAsString());
|
||||
}
|
||||
|
||||
}`
|
||||
)
|
||||
@@ -1,30 +0,0 @@
|
||||
package langs
|
||||
|
||||
type LambdaNodeHelper struct {
|
||||
BaseHelper
|
||||
}
|
||||
|
||||
func (lh *LambdaNodeHelper) BuildFromImage() string {
|
||||
return "funcy/lambda:node-4"
|
||||
}
|
||||
|
||||
func (lh *LambdaNodeHelper) IsMultiStage() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (lh *LambdaNodeHelper) Cmd() string {
|
||||
return "func.handler"
|
||||
}
|
||||
|
||||
func (h *LambdaNodeHelper) DockerfileBuildCmds() []string {
|
||||
r := []string{}
|
||||
if exists("package.json") {
|
||||
r = append(r,
|
||||
"ADD package.json /function/",
|
||||
"RUN npm install",
|
||||
)
|
||||
}
|
||||
// single stage build for this one, so add files
|
||||
r = append(r, "ADD . /function/")
|
||||
return r
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package langs
|
||||
|
||||
type NodeLangHelper struct {
|
||||
BaseHelper
|
||||
}
|
||||
|
||||
func (lh *NodeLangHelper) BuildFromImage() string {
|
||||
return "funcy/node:dev"
|
||||
}
|
||||
func (lh *NodeLangHelper) RunFromImage() string {
|
||||
return "funcy/node"
|
||||
}
|
||||
|
||||
func (lh *NodeLangHelper) Entrypoint() string {
|
||||
return "node func.js"
|
||||
}
|
||||
|
||||
func (h *NodeLangHelper) DockerfileBuildCmds() []string {
|
||||
r := []string{}
|
||||
if exists("package.json") {
|
||||
r = append(r,
|
||||
"ADD package.json /function/",
|
||||
"RUN npm install",
|
||||
)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (h *NodeLangHelper) DockerfileCopyCmds() []string {
|
||||
return []string{
|
||||
"ADD . /function/",
|
||||
"COPY --from=build-stage /function/node_modules/ /function/node_modules/",
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package langs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PhpLangHelper struct {
|
||||
BaseHelper
|
||||
}
|
||||
|
||||
func (lh *PhpLangHelper) BuildFromImage() string {
|
||||
return "funcy/php:dev"
|
||||
}
|
||||
func (lh *PhpLangHelper) Entrypoint() string {
|
||||
return "php func.php"
|
||||
}
|
||||
|
||||
func (lh *PhpLangHelper) HasPreBuild() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (lh *PhpLangHelper) PreBuild() error {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists(filepath.Join(wd, "composer.json")) {
|
||||
return nil
|
||||
}
|
||||
|
||||
pbcmd := fmt.Sprintf("docker run --rm -v %s:/worker -w /worker funcy/php:dev composer install", wd)
|
||||
fmt.Println("Running prebuild command:", pbcmd)
|
||||
parts := strings.Fields(pbcmd)
|
||||
head := parts[0]
|
||||
parts = parts[1:len(parts)]
|
||||
cmd := exec.Command(head, parts...)
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
return dockerBuildError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lh *PhpLangHelper) AfterBuild() error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package langs
|
||||
|
||||
type PythonLangHelper struct {
|
||||
BaseHelper
|
||||
}
|
||||
|
||||
func (lh *PythonLangHelper) BuildFromImage() string {
|
||||
return "funcy/python:2-dev"
|
||||
}
|
||||
|
||||
func (lh *PythonLangHelper) RunFromImage() string {
|
||||
return "funcy/python:2-dev"
|
||||
}
|
||||
|
||||
func (lh *PythonLangHelper) Entrypoint() string {
|
||||
return "python2 func.py"
|
||||
}
|
||||
|
||||
func (h *PythonLangHelper) DockerfileBuildCmds() []string {
|
||||
r := []string{}
|
||||
if exists("requirements.txt") {
|
||||
r = append(r,
|
||||
"ADD requirements.txt /function/",
|
||||
"RUN pip install -r requirements.txt",
|
||||
"ADD . /function/",
|
||||
)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (h *PythonLangHelper) IsMultiStage() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// The multi-stage build didn't work, pip seems to be required for it to load the modules
|
||||
// func (h *PythonLangHelper) DockerfileCopyCmds() []string {
|
||||
// return []string{
|
||||
// "ADD . /function/",
|
||||
// "COPY --from=build-stage /root/.cache/pip/ /root/.cache/pip/",
|
||||
// }
|
||||
// }
|
||||
@@ -1,35 +0,0 @@
|
||||
package langs
|
||||
|
||||
type RubyLangHelper struct {
|
||||
BaseHelper
|
||||
}
|
||||
|
||||
func (lh *RubyLangHelper) BuildFromImage() string {
|
||||
return "funcy/ruby:dev"
|
||||
}
|
||||
|
||||
func (lh *RubyLangHelper) RunFromImage() string {
|
||||
return "funcy/ruby"
|
||||
}
|
||||
|
||||
func (h *RubyLangHelper) DockerfileBuildCmds() []string {
|
||||
r := []string{}
|
||||
if exists("Gemfile") {
|
||||
r = append(r,
|
||||
"ADD Gemfile* /function/",
|
||||
"RUN bundle install",
|
||||
)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (h *RubyLangHelper) DockerfileCopyCmds() []string {
|
||||
return []string{
|
||||
"COPY --from=build-stage /usr/lib/ruby/gems/ /usr/lib/ruby/gems/", // skip this if no Gemfile? Does it matter?
|
||||
"ADD . /function/",
|
||||
}
|
||||
}
|
||||
|
||||
func (lh *RubyLangHelper) Entrypoint() string {
|
||||
return "ruby func.rb"
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package langs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
type RustLangHelper struct {
|
||||
BaseHelper
|
||||
}
|
||||
|
||||
func (lh *RustLangHelper) BuildFromImage() string {
|
||||
return "funcy/rust:dev"
|
||||
}
|
||||
func (lh *RustLangHelper) Entrypoint() string {
|
||||
return "/function/target/release/func"
|
||||
}
|
||||
|
||||
func (lh *RustLangHelper) HasPreBuild() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// PreBuild for rust builds the binary so the final image can be as small as possible
|
||||
func (lh *RustLangHelper) PreBuild() error {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command(
|
||||
"docker", "run",
|
||||
"--rm", "-v",
|
||||
wd+":/app", "-w", "/app", "corey/rust-alpine",
|
||||
"/bin/sh", "-c", "cargo build --release",
|
||||
)
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
return dockerBuildError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lh *RustLangHelper) AfterBuild() error {
|
||||
return os.RemoveAll("target")
|
||||
}
|
||||
53
cli/logs.go
53
cli/logs.go
@@ -1,53 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
client "github.com/fnproject/fn/cli/client"
|
||||
fnclient "github.com/funcy/functions_go/client"
|
||||
apicall "github.com/funcy/functions_go/client/operations"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type logsCmd struct {
|
||||
client *fnclient.Functions
|
||||
}
|
||||
|
||||
func logs() cli.Command {
|
||||
c := logsCmd{client: client.APIClient()}
|
||||
|
||||
return cli.Command{
|
||||
Name: "logs",
|
||||
Usage: "manage function calls for apps",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "get",
|
||||
Aliases: []string{"g"},
|
||||
Usage: "get function call log info per app",
|
||||
ArgsUsage: "<app> <call-id>",
|
||||
Action: c.get,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (log *logsCmd) get(ctx *cli.Context) error {
|
||||
app, callID := ctx.Args().Get(0), ctx.Args().Get(1)
|
||||
params := apicall.GetAppsAppCallsCallLogParams{
|
||||
Call: callID,
|
||||
App: app,
|
||||
Context: context.Background(),
|
||||
}
|
||||
resp, err := log.client.Operations.GetAppsAppCallsCallLog(¶ms)
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apicall.GetAppsAppCallsCallLogNotFound:
|
||||
return fmt.Errorf("error: %v", e.Payload.Error.Message)
|
||||
default:
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
fmt.Print(resp.Payload.Log.Log)
|
||||
return nil
|
||||
}
|
||||
154
cli/main.go
154
cli/main.go
@@ -1,154 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
functions "github.com/funcy/functions_go"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var aliases = map[string]cli.Command{
|
||||
"build": build(),
|
||||
"bump": bump(),
|
||||
"deploy": deploy(),
|
||||
"push": push(),
|
||||
"run": run(),
|
||||
"call": call(),
|
||||
"calls": calls(),
|
||||
"logs": logs(),
|
||||
}
|
||||
|
||||
func aliasesFn() []cli.Command {
|
||||
cmds := []cli.Command{}
|
||||
for alias, cmd := range aliases {
|
||||
cmd.Name = alias
|
||||
cmd.Hidden = true
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
return cmds
|
||||
}
|
||||
|
||||
func newFn() *cli.App {
|
||||
app := cli.NewApp()
|
||||
app.Name = "fn"
|
||||
app.Version = Version
|
||||
app.Authors = []cli.Author{{Name: "Oracle Corporation"}}
|
||||
app.Description = "Fn command line tool"
|
||||
app.UsageText = `Check the docs at https://github.com/fnproject/fn/blob/master/fn/README.md`
|
||||
|
||||
cli.AppHelpTemplate = `{{.Name}} {{.Version}}{{if .Description}}
|
||||
|
||||
{{.Description}}{{end}}
|
||||
|
||||
USAGE:
|
||||
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
|
||||
|
||||
ENVIRONMENT VARIABLES:
|
||||
API_URL - Oracle Functions remote API address{{if .VisibleCommands}}
|
||||
|
||||
COMMANDS:{{range .VisibleCategories}}{{if .Name}}
|
||||
{{.Name}}:{{end}}{{range .VisibleCommands}}
|
||||
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
|
||||
|
||||
ALIASES:
|
||||
build (images build)
|
||||
bump (images bump)
|
||||
deploy (images deploy)
|
||||
run (images run)
|
||||
call (routes call)
|
||||
push (images push)
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
{{range $index, $option := .VisibleFlags}}{{if $index}}
|
||||
{{end}}{{$option}}{{end}}{{end}}
|
||||
`
|
||||
|
||||
app.CommandNotFound = func(c *cli.Context, cmd string) {
|
||||
fmt.Fprintf(os.Stderr, "command not found: %v\n", cmd)
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
startCmd(),
|
||||
updateCmd(),
|
||||
initFn(),
|
||||
apps(),
|
||||
routes(),
|
||||
images(),
|
||||
lambda(),
|
||||
version(),
|
||||
calls(),
|
||||
logs(),
|
||||
testfn(),
|
||||
}
|
||||
app.Commands = append(app.Commands, aliasesFn()...)
|
||||
|
||||
prepareCmdArgsValidation(app.Commands)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func parseArgs(c *cli.Context) ([]string, []string) {
|
||||
args := strings.Split(c.Command.ArgsUsage, " ")
|
||||
var reqArgs []string
|
||||
var optArgs []string
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, "[") {
|
||||
optArgs = append(optArgs, arg)
|
||||
} else if strings.Trim(arg, " ") != "" {
|
||||
reqArgs = append(reqArgs, arg)
|
||||
}
|
||||
}
|
||||
return reqArgs, optArgs
|
||||
}
|
||||
|
||||
func prepareCmdArgsValidation(cmds []cli.Command) {
|
||||
// TODO: refactor fn to use urfave/cli.v2
|
||||
// v1 doesn't let us validate args before the cmd.Action
|
||||
|
||||
for i, cmd := range cmds {
|
||||
prepareCmdArgsValidation(cmd.Subcommands)
|
||||
if cmd.Action == nil {
|
||||
continue
|
||||
}
|
||||
action := cmd.Action
|
||||
cmd.Action = func(c *cli.Context) error {
|
||||
reqArgs, _ := parseArgs(c)
|
||||
if c.NArg() < len(reqArgs) {
|
||||
var help bytes.Buffer
|
||||
cli.HelpPrinter(&help, cli.CommandHelpTemplate, c.Command)
|
||||
return fmt.Errorf("ERROR: Missing required arguments: %s\n\n%s", strings.Join(reqArgs[c.NArg():], " "), help.String())
|
||||
}
|
||||
return cli.HandleAction(action, c)
|
||||
}
|
||||
cmds[i] = cmd
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := newFn()
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
// TODO: this doesn't seem to get called even when an error returns from a command, but maybe urfave is doing a non zero exit anyways? nope: https://github.com/urfave/cli/issues/610
|
||||
fmt.Fprintf(os.Stderr, "Error occurred: %v, exiting...\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func resetBasePath(c *functions.Configuration) error {
|
||||
apiURL := os.Getenv("API_URL")
|
||||
if apiURL == "" {
|
||||
apiURL = "http://localhost:8080"
|
||||
}
|
||||
|
||||
u, err := url.Parse(apiURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Path = "/v1"
|
||||
c.BasePath = u.String()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMainCommands(t *testing.T) {
|
||||
testCommands := []string{
|
||||
"init",
|
||||
"apps",
|
||||
"routes",
|
||||
"images",
|
||||
"lambda",
|
||||
"version",
|
||||
"build",
|
||||
"bump",
|
||||
"deploy",
|
||||
"run",
|
||||
"push",
|
||||
"logs",
|
||||
"calls",
|
||||
"call",
|
||||
}
|
||||
|
||||
fnTestBin := path.Join(os.TempDir(), "fn-test")
|
||||
|
||||
err := exec.Command("go", "build", "-o", fnTestBin).Run()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to build fn: err: %s", err)
|
||||
}
|
||||
|
||||
for _, cmd := range testCommands {
|
||||
res, err := exec.Command(fnTestBin, strings.Split(cmd, " ")...).CombinedOutput()
|
||||
if bytes.Contains(res, []byte("command not found")) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
os.Remove(fnTestBin)
|
||||
}
|
||||
69
cli/push.go
69
cli/push.go
@@ -1,69 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func push() cli.Command {
|
||||
cmd := pushcmd{}
|
||||
var flags []cli.Flag
|
||||
flags = append(flags, cmd.flags()...)
|
||||
return cli.Command{
|
||||
Name: "push",
|
||||
Usage: "push function to Docker Hub",
|
||||
Flags: flags,
|
||||
Action: cmd.push,
|
||||
}
|
||||
}
|
||||
|
||||
type pushcmd struct {
|
||||
verbose bool
|
||||
registry string
|
||||
}
|
||||
|
||||
func (cmd *pushcmd) Registry() string {
|
||||
return cmd.registry
|
||||
}
|
||||
|
||||
func (p *pushcmd) flags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "v",
|
||||
Usage: "verbose mode",
|
||||
Destination: &p.verbose,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "registry",
|
||||
Usage: "Sets the Docker owner for images and optionally the registry. This will be prefixed to your function name for pushing to Docker registries. eg: `--registry username` will set your Docker Hub owner. `--registry registry.hub.docker.com/username` will set the registry and owner.",
|
||||
Destination: &p.registry,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// push will take the found function and check for the presence of a
|
||||
// Dockerfile, and run a three step process: parse functions file,
|
||||
// push the container, and finally it will update function's route. Optionally,
|
||||
// the route can be overriden inside the functions file.
|
||||
func (p *pushcmd) push(c *cli.Context) error {
|
||||
setRegistryEnv(p)
|
||||
|
||||
ff, err := loadFuncfile()
|
||||
if err != nil {
|
||||
if _, ok := err.(*notFoundError); ok {
|
||||
return errors.New("error: image name is missing or no function file found")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("pushing", ff.ImageName())
|
||||
|
||||
if err := dockerpush(ff); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Function %v pushed successfully to Docker Hub.\n", ff.ImageName())
|
||||
return nil
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
# ensure working dir is clean
|
||||
git status
|
||||
if [[ -z $(git status -s) ]]
|
||||
then
|
||||
echo "tree is clean"
|
||||
else
|
||||
echo "tree is dirty, please commit changes before running this"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git pull
|
||||
|
||||
version_file="version.go"
|
||||
if [ -z $(grep -m1 -Po '(?<= = ")\d+\.\d+\.\d+' $version_file) ]; then
|
||||
echo "did not find semantic version in $version_file"
|
||||
exit 1
|
||||
fi
|
||||
perl -i -pe 's/(?<= = ")\d+\.\d+\.\K(\d+)/$1+1/e' $version_file
|
||||
version=$(grep -m1 -Po '(?<= = ")\d+\.\d+\.\d+' $version_file)
|
||||
echo "Version: $version"
|
||||
|
||||
#cd lambda
|
||||
#./release.sh
|
||||
#cd ..
|
||||
|
||||
# make dep
|
||||
make release
|
||||
|
||||
tag="fn-$version"
|
||||
git add -u
|
||||
git commit -m "fn tool: $version release [skip ci]"
|
||||
# todo: might make sense to move this into it's own repo so it can have it's own versioning at some point
|
||||
git tag -f -a $tag -m "fn version $version"
|
||||
git push
|
||||
git push origin $tag
|
||||
|
||||
# For GitHub
|
||||
url='https://api.github.com/repos/fnproject/cli/releases'
|
||||
output=$(curl -s -u $GH_DEPLOY_USER:$GH_DEPLOY_KEY -d "{\"tag_name\": \"$version\", \"name\": \"$version\"}" $url)
|
||||
upload_url=$(echo "$output" | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["upload_url"]' | sed -E "s/\{.*//")
|
||||
html_url=$(echo "$output" | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["html_url"]')
|
||||
curl --data-binary "@fn_linux" -H "Content-Type: application/octet-stream" -u $GH_DEPLOY_USER:$GH_DEPLOY_KEY $upload_url\?name\=fn_linux >/dev/null
|
||||
curl --data-binary "@fn_mac" -H "Content-Type: application/octet-stream" -u $GH_DEPLOY_USER:$GH_DEPLOY_KEY $upload_url\?name\=fn_mac >/dev/null
|
||||
curl --data-binary "@fn.exe" -H "Content-Type: application/octet-stream" -u $GH_DEPLOY_USER:$GH_DEPLOY_KEY $upload_url\?name\=fn.exe >/dev/null
|
||||
curl --data-binary "@fn_alpine" -H "Content-Type: application/octet-stream" -u $GH_DEPLOY_USER:$GH_DEPLOY_KEY $upload_url\?name\=fn_alpine >/dev/null
|
||||
|
||||
# TODO: Add the download URLS to install.sh. Maybe we should make a template to generate install.sh
|
||||
# TODO: Download URL's are in the output vars above under "url". Eg: "url":"/uploads/9a1848c5ebf2b83f8b055ac0e50e5232/fn.exe"
|
||||
# sed "s/release=.*/release=\"$version\"/g" install.sh > install.sh.tmp
|
||||
# mv install.sh.tmp install.sh
|
||||
# TODO: then git commit and push? Would be nice to do that along with the vrsion git push above
|
||||
536
cli/routes.go
536
cli/routes.go
@@ -1,536 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
client "github.com/fnproject/fn/cli/client"
|
||||
fnclient "github.com/funcy/functions_go/client"
|
||||
apiroutes "github.com/funcy/functions_go/client/routes"
|
||||
fnmodels "github.com/funcy/functions_go/models"
|
||||
"github.com/jmoiron/jsonq"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type routesCmd struct {
|
||||
client *fnclient.Functions
|
||||
}
|
||||
|
||||
var routeFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "image,i",
|
||||
Usage: "image name",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "memory,m",
|
||||
Usage: "memory in MiB",
|
||||
Value: uint64(128),
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "type,t",
|
||||
Usage: "route type - sync or async",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "config,c",
|
||||
Usage: "route configuration",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "headers",
|
||||
Usage: "route response headers",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "format,f",
|
||||
Usage: "hot container IO format - default or http",
|
||||
Value: "default",
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Usage: "route timeout (eg. 30s)",
|
||||
Value: 30 * time.Second,
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "idle-timeout",
|
||||
Usage: "route idle timeout (eg. 30s)",
|
||||
Value: 30 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
var updateRouteFlags = append(routeFlags,
|
||||
cli.BoolFlag{
|
||||
Name: "ignore-fn-file",
|
||||
Usage: "defines whether skip func file or not",
|
||||
})
|
||||
|
||||
var callFnFlags = append(runflags(),
|
||||
cli.BoolFlag{
|
||||
Name: "display-call-id",
|
||||
Usage: "whether display call ID or not",
|
||||
},
|
||||
)
|
||||
|
||||
func routes() cli.Command {
|
||||
|
||||
r := routesCmd{client: client.APIClient()}
|
||||
|
||||
return cli.Command{
|
||||
Name: "routes",
|
||||
Usage: "manage routes",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "call",
|
||||
Usage: "call a route",
|
||||
ArgsUsage: "<app> </path> [image]",
|
||||
Action: r.call,
|
||||
Flags: callFnFlags,
|
||||
},
|
||||
{
|
||||
Name: "list",
|
||||
Aliases: []string{"l"},
|
||||
Usage: "list routes for `app`",
|
||||
ArgsUsage: "<app>",
|
||||
Action: r.list,
|
||||
},
|
||||
{
|
||||
Name: "create",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "create a route in an `app`",
|
||||
ArgsUsage: "<app> </path>",
|
||||
Action: r.create,
|
||||
Flags: routeFlags,
|
||||
},
|
||||
{
|
||||
Name: "update",
|
||||
Aliases: []string{"u"},
|
||||
Usage: "update a route in an `app`",
|
||||
ArgsUsage: "<app> </path>",
|
||||
Action: r.update,
|
||||
Flags: updateRouteFlags,
|
||||
},
|
||||
{
|
||||
Name: "config",
|
||||
Usage: "operate a route configuration set",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "set",
|
||||
Aliases: []string{"s"},
|
||||
Usage: "store a configuration key for this route",
|
||||
ArgsUsage: "<app> </path> <key> <value>",
|
||||
Action: r.configSet,
|
||||
},
|
||||
{
|
||||
Name: "unset",
|
||||
Aliases: []string{"u"},
|
||||
Usage: "remove a configuration key for this route",
|
||||
ArgsUsage: "<app> </path> <key>",
|
||||
Action: r.configUnset,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "delete",
|
||||
Aliases: []string{"d"},
|
||||
Usage: "delete a route from `app`",
|
||||
ArgsUsage: "<app> </path>",
|
||||
Action: r.delete,
|
||||
},
|
||||
{
|
||||
Name: "inspect",
|
||||
Aliases: []string{"i"},
|
||||
Usage: "retrieve one or all routes properties",
|
||||
ArgsUsage: "<app> </path> [property.[key]]",
|
||||
Action: r.inspect,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func call() cli.Command {
|
||||
r := routesCmd{client: client.APIClient()}
|
||||
|
||||
return cli.Command{
|
||||
Name: "call",
|
||||
Usage: "call a remote function",
|
||||
ArgsUsage: "<app> </path>",
|
||||
Flags: callFnFlags,
|
||||
Action: r.call,
|
||||
}
|
||||
}
|
||||
|
||||
func cleanRoutePath(p string) string {
|
||||
p = path.Clean(p)
|
||||
if !path.IsAbs(p) {
|
||||
p = "/" + p
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (a *routesCmd) list(c *cli.Context) error {
|
||||
appName := c.Args().Get(0)
|
||||
|
||||
resp, err := a.client.Routes.GetAppsAppRoutes(&apiroutes.GetAppsAppRoutesParams{
|
||||
Context: context.Background(),
|
||||
App: appName,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apiroutes.GetAppsAppRoutesNotFound:
|
||||
return fmt.Errorf("error: %s", e.Payload.Error.Message)
|
||||
case *apiroutes.GetAppsAppRoutesDefault:
|
||||
return fmt.Errorf("unexpected error: %s", e.Payload.Error.Message)
|
||||
default:
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
|
||||
fmt.Fprint(w, "path", "\t", "image", "\t", "endpoint", "\n")
|
||||
for _, route := range resp.Payload.Routes {
|
||||
endpoint := path.Join(client.Host(), "r", appName, route.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing functions route path: %s", err)
|
||||
}
|
||||
|
||||
fmt.Fprint(w, route.Path, "\t", route.Image, "\t", endpoint, "\n")
|
||||
}
|
||||
w.Flush()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *routesCmd) call(c *cli.Context) error {
|
||||
appName := c.Args().Get(0)
|
||||
route := cleanRoutePath(c.Args().Get(1))
|
||||
|
||||
u := url.URL{
|
||||
Scheme: "http",
|
||||
Host: client.Host(),
|
||||
}
|
||||
u.Path = path.Join(u.Path, "r", appName, route)
|
||||
content := stdin()
|
||||
|
||||
return client.CallFN(u.String(), content, os.Stdout, c.String("method"), c.StringSlice("e"), c.Bool("display-call-id"))
|
||||
}
|
||||
|
||||
func routeWithFlags(c *cli.Context, rt *fnmodels.Route) {
|
||||
if i := c.String("image"); i != "" {
|
||||
rt.Image = i
|
||||
}
|
||||
|
||||
if f := c.String("format"); f != "" {
|
||||
rt.Format = f
|
||||
}
|
||||
|
||||
if t := c.String("type"); t != "" {
|
||||
rt.Type = t
|
||||
}
|
||||
|
||||
if m := c.Uint64("memory"); m > 0 {
|
||||
rt.Memory = m
|
||||
}
|
||||
|
||||
if t := c.Duration("timeout"); t > 0 {
|
||||
to := int32(t.Seconds())
|
||||
rt.Timeout = &to
|
||||
}
|
||||
|
||||
if t := c.Duration("idle-timeout"); t > 0 {
|
||||
to := int32(t.Seconds())
|
||||
rt.IDLETimeout = &to
|
||||
}
|
||||
|
||||
if len(c.StringSlice("headers")) > 0 {
|
||||
headers := map[string][]string{}
|
||||
for _, header := range c.StringSlice("headers") {
|
||||
parts := strings.Split(header, "=")
|
||||
headers[parts[0]] = strings.Split(parts[1], ";")
|
||||
}
|
||||
rt.Headers = headers
|
||||
}
|
||||
|
||||
if len(c.StringSlice("config")) > 0 {
|
||||
rt.Config = extractEnvConfig(c.StringSlice("config"))
|
||||
}
|
||||
}
|
||||
|
||||
func routeWithFuncFile(ff *funcfile, rt *fnmodels.Route) error {
|
||||
var err error
|
||||
if ff == nil {
|
||||
ff, err = loadFuncfile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if ff.ImageName() != "" { // args take precedence
|
||||
rt.Image = ff.ImageName()
|
||||
}
|
||||
if ff.Format != "" {
|
||||
rt.Format = ff.Format
|
||||
}
|
||||
if ff.Timeout != nil {
|
||||
rt.Timeout = ff.Timeout
|
||||
}
|
||||
if rt.Path == "" && ff.Path != "" {
|
||||
rt.Path = ff.Path
|
||||
}
|
||||
if rt.Type == "" && ff.Type != "" {
|
||||
rt.Type = ff.Type
|
||||
}
|
||||
if ff.Memory != 0 {
|
||||
rt.Memory = ff.Memory
|
||||
}
|
||||
if rt.IDLETimeout != nil {
|
||||
rt.IDLETimeout = ff.IDLETimeout
|
||||
}
|
||||
if len(rt.Headers) != 0 {
|
||||
rt.Headers = ff.Headers
|
||||
}
|
||||
if len(rt.Config) != 0 {
|
||||
rt.Config = ff.Config
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *routesCmd) create(c *cli.Context) error {
|
||||
appName := c.Args().Get(0)
|
||||
route := cleanRoutePath(c.Args().Get(1))
|
||||
|
||||
rt := &fnmodels.Route{}
|
||||
rt.Path = route
|
||||
rt.Image = c.Args().Get(2)
|
||||
|
||||
if err := routeWithFuncFile(nil, rt); err != nil {
|
||||
return fmt.Errorf("error getting route info: %s", err)
|
||||
}
|
||||
|
||||
routeWithFlags(c, rt)
|
||||
|
||||
if rt.Path == "" {
|
||||
return errors.New("route path is missing")
|
||||
}
|
||||
if rt.Image == "" {
|
||||
return errors.New("no image specified")
|
||||
}
|
||||
|
||||
return a.postRoute(c, appName, rt)
|
||||
}
|
||||
|
||||
func (a *routesCmd) postRoute(c *cli.Context, appName string, rt *fnmodels.Route) error {
|
||||
|
||||
body := &fnmodels.RouteWrapper{
|
||||
Route: rt,
|
||||
}
|
||||
|
||||
resp, err := a.client.Routes.PostAppsAppRoutes(&apiroutes.PostAppsAppRoutesParams{
|
||||
Context: context.Background(),
|
||||
App: appName,
|
||||
Body: body,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apiroutes.PostAppsAppRoutesBadRequest:
|
||||
return fmt.Errorf("error: %s", e.Payload.Error.Message)
|
||||
case *apiroutes.PostAppsAppRoutesConflict:
|
||||
return fmt.Errorf("error: %s", e.Payload.Error.Message)
|
||||
case *apiroutes.PostAppsAppRoutesDefault:
|
||||
return fmt.Errorf("unexpected error: %s", e.Payload.Error.Message)
|
||||
default:
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(resp.Payload.Route.Path, "created with", resp.Payload.Route.Image)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *routesCmd) patchRoute(c *cli.Context, appName, routePath string, r *fnmodels.Route) error {
|
||||
_, err := a.client.Routes.PatchAppsAppRoutesRoute(&apiroutes.PatchAppsAppRoutesRouteParams{
|
||||
Context: context.Background(),
|
||||
App: appName,
|
||||
Route: routePath,
|
||||
Body: &fnmodels.RouteWrapper{Route: r},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apiroutes.PatchAppsAppRoutesRouteBadRequest:
|
||||
return fmt.Errorf("error: %s", e.Payload.Error.Message)
|
||||
case *apiroutes.PatchAppsAppRoutesRouteNotFound:
|
||||
return fmt.Errorf("error: %s", e.Payload.Error.Message)
|
||||
case *apiroutes.PatchAppsAppRoutesRouteDefault:
|
||||
return fmt.Errorf("unexpected error: %s", e.Payload.Error.Message)
|
||||
default:
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *routesCmd) putRoute(c *cli.Context, appName, routePath string, r *fnmodels.Route) error {
|
||||
_, err := a.client.Routes.PutAppsAppRoutesRoute(&apiroutes.PutAppsAppRoutesRouteParams{
|
||||
Context: context.Background(),
|
||||
App: appName,
|
||||
Route: routePath,
|
||||
Body: &fnmodels.RouteWrapper{Route: r},
|
||||
})
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apiroutes.PutAppsAppRoutesRouteBadRequest:
|
||||
return fmt.Errorf("error: %s", e.Payload.Error.Message)
|
||||
case *apiroutes.PutAppsAppRoutesRouteDefault:
|
||||
return fmt.Errorf("unexpected error: %s", e.Payload.Error.Message)
|
||||
default:
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *routesCmd) update(c *cli.Context) error {
|
||||
appName := c.Args().Get(0)
|
||||
route := cleanRoutePath(c.Args().Get(1))
|
||||
|
||||
rt := &fnmodels.Route{}
|
||||
|
||||
if !c.Bool("ignore-fn-file") {
|
||||
if err := routeWithFuncFile(nil, rt); err != nil {
|
||||
return fmt.Errorf("error updating route: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
routeWithFlags(c, rt)
|
||||
|
||||
err := a.patchRoute(c, appName, route, rt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(appName, route, "updated")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *routesCmd) configSet(c *cli.Context) error {
|
||||
appName := c.Args().Get(0)
|
||||
route := cleanRoutePath(c.Args().Get(1))
|
||||
key := c.Args().Get(2)
|
||||
value := c.Args().Get(3)
|
||||
|
||||
patchRoute := fnmodels.Route{
|
||||
Config: make(map[string]string),
|
||||
}
|
||||
|
||||
patchRoute.Config[key] = value
|
||||
|
||||
err := a.patchRoute(c, appName, route, &patchRoute)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(appName, route, "updated", key, "with", value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *routesCmd) configUnset(c *cli.Context) error {
|
||||
appName := c.Args().Get(0)
|
||||
route := cleanRoutePath(c.Args().Get(1))
|
||||
key := c.Args().Get(2)
|
||||
|
||||
patchRoute := fnmodels.Route{
|
||||
Config: make(map[string]string),
|
||||
}
|
||||
|
||||
patchRoute.Config[key] = ""
|
||||
|
||||
err := a.patchRoute(c, appName, route, &patchRoute)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("removed key '%s' from the route '%s%s'", key, appName, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *routesCmd) inspect(c *cli.Context) error {
|
||||
appName := c.Args().Get(0)
|
||||
route := cleanRoutePath(c.Args().Get(1))
|
||||
prop := c.Args().Get(2)
|
||||
|
||||
resp, err := a.client.Routes.GetAppsAppRoutesRoute(&apiroutes.GetAppsAppRoutesRouteParams{
|
||||
Context: context.Background(),
|
||||
App: appName,
|
||||
Route: route,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apiroutes.GetAppsAppRoutesRouteNotFound:
|
||||
return fmt.Errorf("error: %s", e.Payload.Error.Message)
|
||||
case *apiroutes.GetAppsAppRoutesRouteDefault:
|
||||
return fmt.Errorf("unexpected error: %s", e.Payload.Error.Message)
|
||||
default:
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", "\t")
|
||||
|
||||
if prop == "" {
|
||||
enc.Encode(resp.Payload.Route)
|
||||
return nil
|
||||
}
|
||||
|
||||
data, err := json.Marshal(resp.Payload.Route)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to inspect route: %s", err)
|
||||
}
|
||||
var inspect map[string]interface{}
|
||||
err = json.Unmarshal(data, &inspect)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to inspect route: %s", err)
|
||||
}
|
||||
|
||||
jq := jsonq.NewQuery(inspect)
|
||||
field, err := jq.Interface(strings.Split(prop, ".")...)
|
||||
if err != nil {
|
||||
return errors.New("failed to inspect that route's field")
|
||||
}
|
||||
enc.Encode(field)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *routesCmd) delete(c *cli.Context) error {
|
||||
appName := c.Args().Get(0)
|
||||
route := cleanRoutePath(c.Args().Get(1))
|
||||
|
||||
_, err := a.client.Routes.DeleteAppsAppRoutesRoute(&apiroutes.DeleteAppsAppRoutesRouteParams{
|
||||
Context: context.Background(),
|
||||
App: appName,
|
||||
Route: route,
|
||||
})
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *apiroutes.DeleteAppsAppRoutesRouteNotFound:
|
||||
return fmt.Errorf("error: %s", e.Payload.Error.Message)
|
||||
case *apiroutes.DeleteAppsAppRoutesRouteDefault:
|
||||
return fmt.Errorf("unexpected error: %s", e.Payload.Error.Message)
|
||||
default:
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(appName, route, "deleted")
|
||||
return nil
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/fnproject/fn/cli/client"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEnvAsHeader(t *testing.T) {
|
||||
const expectedValue = "v=v"
|
||||
os.Setenv("k", expectedValue)
|
||||
|
||||
cases := [][]string{
|
||||
nil,
|
||||
[]string{},
|
||||
[]string{"k"},
|
||||
}
|
||||
for _, selectedEnv := range cases {
|
||||
req, _ := http.NewRequest("GET", "http://www.example.com", nil)
|
||||
client.EnvAsHeader(req, selectedEnv)
|
||||
if found := req.Header.Get("k"); found != expectedValue {
|
||||
t.Errorf("not found expected header: %v", found)
|
||||
}
|
||||
}
|
||||
}
|
||||
209
cli/run.go
209
cli/run.go
@@ -1,209 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultFormat = "default"
|
||||
HttpFormat = "http"
|
||||
LocalTestURL = "http://localhost:8080/myapp/hello"
|
||||
)
|
||||
|
||||
func run() cli.Command {
|
||||
r := runCmd{}
|
||||
|
||||
return cli.Command{
|
||||
Name: "run",
|
||||
Usage: "run a function locally",
|
||||
ArgsUsage: "[username/image:tag]",
|
||||
Flags: append(runflags(), []cli.Flag{}...),
|
||||
Action: r.run,
|
||||
}
|
||||
}
|
||||
|
||||
type runCmd struct{}
|
||||
|
||||
func runflags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringSliceFlag{
|
||||
Name: "env, e",
|
||||
Usage: "select environment variables to be sent to function",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "link",
|
||||
Usage: "select container links for the function",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "method",
|
||||
Usage: "http method for function",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format to use. `default` and `http` (hot) formats currently supported.",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "runs",
|
||||
Usage: "for hot functions only, will call the function `runs` times in a row.",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "memory",
|
||||
Usage: "RAM to allocate for function, Units: MB",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *runCmd) run(c *cli.Context) error {
|
||||
// First, build it
|
||||
err := c.App.Command("build").Run(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var ff *funcfile
|
||||
// if image name is passed in, it will run that image
|
||||
image := c.Args().First()
|
||||
if image == "" {
|
||||
ff, err = loadFuncfile()
|
||||
if err != nil {
|
||||
if _, ok := err.(*notFoundError); ok {
|
||||
return errors.New("error: image name is missing or no function file found")
|
||||
}
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
ff = &funcfile{
|
||||
Name: image,
|
||||
}
|
||||
}
|
||||
|
||||
// means no memory specified through CLI args
|
||||
// memory from func.yaml applied
|
||||
if c.Uint64("memory") != 0 {
|
||||
ff.Memory = c.Uint64("memory")
|
||||
}
|
||||
|
||||
return runff(ff, stdin(), os.Stdout, os.Stderr, c.String("method"), c.StringSlice("e"), c.StringSlice("link"), c.String("format"), c.Int("runs"))
|
||||
}
|
||||
|
||||
// TODO: share all this stuff with the Docker driver in server or better yet, actually use the Docker driver
|
||||
func runff(ff *funcfile, stdin io.Reader, stdout, stderr io.Writer, method string, envVars []string, links []string, format string, runs int) error {
|
||||
sh := []string{"docker", "run", "--rm", "-i", fmt.Sprintf("--memory=%dm", ff.Memory)}
|
||||
|
||||
var err error
|
||||
var env []string // env for the shelled out docker run command
|
||||
var runEnv []string // env to pass into the container via -e's
|
||||
|
||||
if method == "" {
|
||||
if stdin == nil {
|
||||
method = "GET"
|
||||
} else {
|
||||
method = "POST"
|
||||
}
|
||||
}
|
||||
if format == "" {
|
||||
format = DefaultFormat
|
||||
}
|
||||
// Add expected env vars that service will add
|
||||
runEnv = append(runEnv, kvEq("METHOD", method))
|
||||
runEnv = append(runEnv, kvEq("REQUEST_URL", LocalTestURL))
|
||||
runEnv = append(runEnv, kvEq("APP_NAME", "myapp"))
|
||||
runEnv = append(runEnv, kvEq("ROUTE", "/hello")) // TODO: should we change this to PATH ?
|
||||
runEnv = append(runEnv, kvEq("FN_FORMAT", format))
|
||||
runEnv = append(runEnv, kvEq("MEMORY_MB", fmt.Sprintf("%d", ff.Memory)))
|
||||
|
||||
// add user defined envs
|
||||
runEnv = append(runEnv, envVars...)
|
||||
|
||||
for _, l := range links {
|
||||
sh = append(sh, "--link", l)
|
||||
}
|
||||
|
||||
dockerenv := []string{"DOCKER_TLS_VERIFY", "DOCKER_HOST", "DOCKER_CERT_PATH", "DOCKER_MACHINE_NAME"}
|
||||
for _, e := range dockerenv {
|
||||
env = append(env, fmt.Sprint(e, "=", os.Getenv(e)))
|
||||
}
|
||||
|
||||
for _, e := range runEnv {
|
||||
sh = append(sh, "-e", e)
|
||||
}
|
||||
|
||||
if runs <= 0 {
|
||||
runs = 1
|
||||
}
|
||||
|
||||
if ff.Type != "" && ff.Type == "async" {
|
||||
// if async, we'll run this in a separate thread and wait for it to complete
|
||||
// reqID := id.New().String()
|
||||
// I'm starting to think maybe `fn run` locally should work the same whether sync or async? Or how would we allow to test the output?
|
||||
}
|
||||
body := "" // used for hot functions
|
||||
if format == HttpFormat {
|
||||
// let's swap out stdin for http formatted message
|
||||
input := []byte("")
|
||||
if stdin != nil {
|
||||
input, err = ioutil.ReadAll(stdin)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading from stdin: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
for i := 0; i < runs; i++ {
|
||||
// making new request each time since Write closes the body
|
||||
req, err := http.NewRequest(method, LocalTestURL, strings.NewReader(string(input)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating http request: %v", err)
|
||||
}
|
||||
err = req.Write(&b)
|
||||
b.Write([]byte("\n"))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing to byte buffer: %v", err)
|
||||
}
|
||||
body = b.String()
|
||||
// fmt.Println("body:", s)
|
||||
stdin = strings.NewReader(body)
|
||||
}
|
||||
|
||||
sh = append(sh, ff.ImageName())
|
||||
cmd := exec.Command(sh[0], sh[1:]...)
|
||||
cmd.Stdin = stdin
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
// cmd.Env = env
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func extractEnvVar(e string) ([]string, string) {
|
||||
kv := strings.Split(e, "=")
|
||||
name := toEnvName("HEADER", kv[0])
|
||||
sh := []string{"-e", name}
|
||||
var v string
|
||||
if len(kv) > 1 {
|
||||
v = kv[1]
|
||||
} else {
|
||||
v = os.Getenv(kv[0])
|
||||
}
|
||||
return sh, kvEq(name, v)
|
||||
}
|
||||
|
||||
func kvEq(k, v string) string {
|
||||
return fmt.Sprintf("%s=%s", k, v)
|
||||
}
|
||||
|
||||
// From server.toEnvName()
|
||||
func toEnvName(envtype, name string) string {
|
||||
name = strings.ToUpper(strings.Replace(name, "-", "_", -1))
|
||||
return fmt.Sprintf("%s_%s", envtype, name)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func stdin() io.Reader {
|
||||
stat, err := os.Stdin.Stat()
|
||||
if err != nil || (stat.Mode()&os.ModeCharDevice) != 0 {
|
||||
return nil
|
||||
}
|
||||
return os.Stdin
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func stdin() io.Reader {
|
||||
if isTerminal(int(os.Stdin.Fd())) {
|
||||
return nil
|
||||
}
|
||||
return os.Stdin
|
||||
}
|
||||
|
||||
func isTerminal(fd int) bool {
|
||||
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||
procGetConsoleMode := kernel32.NewProc("GetConsoleMode")
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
}
|
||||
80
cli/start.go
80
cli/start.go
@@ -1,80 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func startCmd() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "start",
|
||||
Usage: "start a functions server",
|
||||
Action: start,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "log-level",
|
||||
Usage: "--log-level DEBUG to enable debugging",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func start(c *cli.Context) error {
|
||||
denvs := []string{}
|
||||
if c.String("log-level") != "" {
|
||||
denvs = append(denvs, "GIN_MODE="+c.String("log-level"))
|
||||
}
|
||||
// Socket mount: docker run --rm -it --name functions -v ${PWD}/data:/app/data -v /var/run/docker.sock:/var/run/docker.sock -p 8080:8080 funcy/functions
|
||||
// OR dind: docker run --rm -it --name functions -v ${PWD}/data:/app/data --privileged -p 8080:8080 funcy/functions
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalln("Getwd failed:", err)
|
||||
}
|
||||
args := []string{"run", "--rm", "-i",
|
||||
"--name", "functions",
|
||||
"-v", fmt.Sprintf("%s/data:/app/data", wd),
|
||||
"-v", "/var/run/docker.sock:/var/run/docker.sock",
|
||||
"-p", "8080:8080",
|
||||
}
|
||||
for _, v := range denvs {
|
||||
args = append(args, "-e", v)
|
||||
}
|
||||
args = append(args, functionsDockerImage)
|
||||
cmd := exec.Command("docker", args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
log.Fatalln("starting command failed:", err)
|
||||
}
|
||||
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- cmd.Wait()
|
||||
}()
|
||||
// catch ctrl-c and kill
|
||||
sigC := make(chan os.Signal, 2)
|
||||
signal.Notify(sigC, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
select {
|
||||
case <-sigC:
|
||||
log.Println("interrupt caught, exiting")
|
||||
err = cmd.Process.Kill()
|
||||
if err != nil {
|
||||
log.Println("error: could not kill process:", err)
|
||||
}
|
||||
case err := <-done:
|
||||
if err != nil {
|
||||
log.Println("error: processed finished with error", err)
|
||||
} else {
|
||||
log.Println("process finished gracefully without error")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
32
cli/test.sh
32
cli/test.sh
@@ -1,32 +0,0 @@
|
||||
set -ex
|
||||
|
||||
make build
|
||||
export fn="$(pwd)/fn"
|
||||
$fn --version
|
||||
|
||||
go test $(go list ./... | grep -v /vendor/ | grep -v /tests)
|
||||
|
||||
# This tests all the quickstart commands on the cli on a live server
|
||||
rm -rf tmp
|
||||
mkdir tmp
|
||||
cd tmp
|
||||
funcname="fn-test-go"
|
||||
$fn init --runtime go $DOCKER_USER/$funcname
|
||||
$fn test
|
||||
|
||||
someport=50080
|
||||
docker rm --force functions || true # just in case
|
||||
docker run --name functions -d -v /var/run/docker.sock:/var/run/docker.sock -p $someport:8080 fnproject/functions
|
||||
sleep 10
|
||||
docker logs functions
|
||||
|
||||
export API_URL="http://localhost:$someport"
|
||||
$fn apps l
|
||||
$fn apps create myapp
|
||||
$fn apps l
|
||||
$fn deploy myapp
|
||||
$fn call myapp $funcname
|
||||
|
||||
docker rm --force functions
|
||||
|
||||
cd ..
|
||||
228
cli/testfn.go
228
cli/testfn.go
@@ -1,228 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fnproject/fn/cli/client"
|
||||
functions "github.com/funcy/functions_go"
|
||||
"github.com/onsi/gomega"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type testStruct struct {
|
||||
Tests []fftest `yaml:"tests,omitempty" json:"tests,omitempty"`
|
||||
}
|
||||
|
||||
func testfn() cli.Command {
|
||||
cmd := testcmd{RoutesApi: functions.NewRoutesApi()}
|
||||
return cli.Command{
|
||||
Name: "test",
|
||||
Usage: "run functions test if present",
|
||||
Flags: cmd.flags(),
|
||||
Action: cmd.test,
|
||||
}
|
||||
}
|
||||
|
||||
type testcmd struct {
|
||||
*functions.RoutesApi
|
||||
|
||||
build bool
|
||||
remote string
|
||||
}
|
||||
|
||||
func (t *testcmd) flags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
// cli.BoolFlag{
|
||||
// Name: "b",
|
||||
// Usage: "build before test",
|
||||
// Destination: &t.build,
|
||||
// },
|
||||
cli.StringFlag{
|
||||
Name: "remote",
|
||||
Usage: "run tests by calling the function on Oracle Functions daemon on `appname`",
|
||||
Destination: &t.remote,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *testcmd) test(c *cli.Context) error {
|
||||
gomega.RegisterFailHandler(func(message string, callerSkip ...int) {
|
||||
fmt.Println("In gomega FailHandler:", message)
|
||||
})
|
||||
|
||||
// First, build it
|
||||
err := c.App.Command("build").Run(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ff, err := loadFuncfile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tests []fftest
|
||||
|
||||
// Look for test.json file too
|
||||
tfile := "test.json"
|
||||
if exists(tfile) {
|
||||
f, err := os.Open(tfile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open %s for parsing. Error: %v", tfile, err)
|
||||
}
|
||||
ts := &testStruct{}
|
||||
err = json.NewDecoder(f).Decode(ts)
|
||||
if err != nil {
|
||||
fmt.Println("Invalid tests.json file:", err)
|
||||
return err
|
||||
}
|
||||
tests = ts.Tests
|
||||
} else {
|
||||
tests = ff.Tests
|
||||
}
|
||||
if len(tests) == 0 {
|
||||
return errors.New("no tests found for this function")
|
||||
}
|
||||
|
||||
fmt.Printf("Running %v tests...", len(tests))
|
||||
|
||||
target := ff.ImageName()
|
||||
runtest := runlocaltest
|
||||
if t.remote != "" {
|
||||
if ff.Path == "" {
|
||||
return errors.New("execution of tests on remote server demand that this function has a `path`.")
|
||||
}
|
||||
if err := resetBasePath(t.Configuration); err != nil {
|
||||
return fmt.Errorf("error setting endpoint: %v", err)
|
||||
}
|
||||
baseURL, err := url.Parse(t.Configuration.BasePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing base path: %v", err)
|
||||
}
|
||||
|
||||
u, err := url.Parse("../")
|
||||
u.Path = path.Join(u.Path, "r", t.remote, ff.Path)
|
||||
target = baseURL.ResolveReference(u).String()
|
||||
runtest = runremotetest
|
||||
}
|
||||
|
||||
errorCount := 0
|
||||
fmt.Println("running tests on", ff.ImageName(), ":")
|
||||
for i, tt := range tests {
|
||||
fmt.Printf("\nTest %v\n", i+1)
|
||||
start := time.Now()
|
||||
var err error
|
||||
err = runtest(target, tt.Input, tt.Output, tt.Err, tt.Env)
|
||||
if err != nil {
|
||||
fmt.Print("FAILED")
|
||||
errorCount += 1
|
||||
scanner := bufio.NewScanner(strings.NewReader(err.Error()))
|
||||
for scanner.Scan() {
|
||||
fmt.Println("\t\t", scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "reading test result:", err)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
fmt.Print("PASSED")
|
||||
}
|
||||
fmt.Println(" - ", tt.Name, " (", time.Since(start), ")")
|
||||
|
||||
}
|
||||
fmt.Printf("\n%v tests passed, %v tests failed.\n", len(tests)-errorCount, errorCount)
|
||||
if errorCount > 0 {
|
||||
return errors.New("tests failed, errors found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runlocaltest(target string, in *inputMap, expectedOut *outputMap, expectedErr *string, env map[string]string) error {
|
||||
inBytes, _ := json.Marshal(in.Body)
|
||||
stdin := &bytes.Buffer{}
|
||||
if in != nil {
|
||||
stdin = bytes.NewBuffer(inBytes)
|
||||
}
|
||||
expectedB, _ := json.Marshal(expectedOut.Body)
|
||||
expectedString := string(expectedB)
|
||||
|
||||
// TODO: use the same run as `fn run` so we don't have to dupe all the config and env vars that get passed in
|
||||
var stdout, stderr bytes.Buffer
|
||||
var restrictedEnv []string
|
||||
for k, v := range env {
|
||||
oldv := os.Getenv(k)
|
||||
defer func(oldk, oldv string) {
|
||||
os.Setenv(oldk, oldv)
|
||||
}(k, oldv)
|
||||
os.Setenv(k, v)
|
||||
restrictedEnv = append(restrictedEnv, k)
|
||||
}
|
||||
|
||||
ff := &funcfile{Name: target}
|
||||
if err := runff(ff, stdin, &stdout, &stderr, "", restrictedEnv, nil, DefaultFormat, 1); err != nil {
|
||||
return fmt.Errorf("%v\nstdout:%s\nstderr:%s\n", err, stdout.String(), stderr.String())
|
||||
}
|
||||
|
||||
out := stdout.String()
|
||||
if expectedOut == nil && out != "" {
|
||||
return fmt.Errorf("unexpected output found: %s", out)
|
||||
}
|
||||
if gomega.Expect(out).To(gomega.MatchJSON(expectedString)) {
|
||||
// PASS!
|
||||
return nil
|
||||
}
|
||||
|
||||
// don't think we should test error output, it's just for logging
|
||||
// err := stderr.String()
|
||||
// if expectedErr == nil && err != "" {
|
||||
// return fmt.Errorf("unexpected error output found: %s", err)
|
||||
// } else if expectedErr != nil && *expectedErr != err {
|
||||
// return fmt.Errorf("mismatched error output found.\nexpected (%d bytes):\n%s\ngot (%d bytes):\n%s\n", len(*expectedErr), *expectedErr, len(err), err)
|
||||
// }
|
||||
|
||||
return fmt.Errorf("mismatched output found.\nexpected:\n%s\ngot:\n%s\n", expectedString, out)
|
||||
}
|
||||
|
||||
func runremotetest(target string, in *inputMap, expectedOut *outputMap, expectedErr *string, env map[string]string) error {
|
||||
inBytes, _ := json.Marshal(in)
|
||||
stdin := &bytes.Buffer{}
|
||||
if in != nil {
|
||||
stdin = bytes.NewBuffer(inBytes)
|
||||
}
|
||||
expectedString, _ := json.Marshal(expectedOut.Body)
|
||||
|
||||
var stdout bytes.Buffer
|
||||
|
||||
var restrictedEnv []string
|
||||
for k, v := range env {
|
||||
oldv := os.Getenv(k)
|
||||
defer func(oldk, oldv string) {
|
||||
os.Setenv(oldk, oldv)
|
||||
}(k, oldv)
|
||||
os.Setenv(k, v)
|
||||
restrictedEnv = append(restrictedEnv, k)
|
||||
}
|
||||
if err := client.CallFN(target, stdin, &stdout, "", restrictedEnv, false); err != nil {
|
||||
return fmt.Errorf("%v\nstdout:%s\n", err, stdout.String())
|
||||
}
|
||||
|
||||
out := stdout.String()
|
||||
if expectedOut == nil && out != "" {
|
||||
return fmt.Errorf("unexpected output found: %s", out)
|
||||
}
|
||||
if gomega.Expect(out).To(gomega.MatchJSON(expectedString)) {
|
||||
// PASS!
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func updateCmd() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "update",
|
||||
Usage: "pulls latest functions server",
|
||||
Action: update,
|
||||
}
|
||||
}
|
||||
|
||||
func update(c *cli.Context) error {
|
||||
args := []string{"pull",
|
||||
functionsDockerImage,
|
||||
}
|
||||
cmd := exec.Command("docker", args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
log.Fatalln("starting command failed:", err)
|
||||
}
|
||||
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- cmd.Wait()
|
||||
}()
|
||||
// catch ctrl-c and kill
|
||||
sigC := make(chan os.Signal, 2)
|
||||
signal.Notify(sigC, os.Interrupt, syscall.SIGTERM)
|
||||
select {
|
||||
case <-sigC:
|
||||
log.Println("interrupt caught, exiting")
|
||||
err = cmd.Process.Kill()
|
||||
if err != nil {
|
||||
log.Println("error: could not kill process")
|
||||
}
|
||||
case err := <-done:
|
||||
if err != nil {
|
||||
log.Println("processed finished with error:", err)
|
||||
} else {
|
||||
log.Println("process finished gracefully without error")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
21
cli/vendor/github.com/Azure/go-ansiterm/LICENSE
generated
vendored
21
cli/vendor/github.com/Azure/go-ansiterm/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Microsoft Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
12
cli/vendor/github.com/Azure/go-ansiterm/README.md
generated
vendored
12
cli/vendor/github.com/Azure/go-ansiterm/README.md
generated
vendored
@@ -1,12 +0,0 @@
|
||||
# go-ansiterm
|
||||
|
||||
This is a cross platform Ansi Terminal Emulation library. It reads a stream of Ansi characters and produces the appropriate function calls. The results of the function calls are platform dependent.
|
||||
|
||||
For example the parser might receive "ESC, [, A" as a stream of three characters. This is the code for Cursor Up (http://www.vt100.net/docs/vt510-rm/CUU). The parser then calls the cursor up function (CUU()) on an event handler. The event handler determines what platform specific work must be done to cause the cursor to move up one position.
|
||||
|
||||
The parser (parser.go) is a partial implementation of this state machine (http://vt100.net/emu/vt500_parser.png). There are also two event handler implementations, one for tests (test_event_handler.go) to validate that the expected events are being produced and called, the other is a Windows implementation (winterm/win_event_handler.go).
|
||||
|
||||
See parser_test.go for examples exercising the state machine and generating appropriate function calls.
|
||||
|
||||
-----
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
188
cli/vendor/github.com/Azure/go-ansiterm/constants.go
generated
vendored
188
cli/vendor/github.com/Azure/go-ansiterm/constants.go
generated
vendored
@@ -1,188 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
const LogEnv = "DEBUG_TERMINAL"
|
||||
|
||||
// ANSI constants
|
||||
// References:
|
||||
// -- http://www.ecma-international.org/publications/standards/Ecma-048.htm
|
||||
// -- http://man7.org/linux/man-pages/man4/console_codes.4.html
|
||||
// -- http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html
|
||||
// -- http://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// -- http://vt100.net/emu/dec_ansi_parser
|
||||
// -- http://vt100.net/emu/vt500_parser.svg
|
||||
// -- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
||||
// -- http://www.inwap.com/pdp10/ansicode.txt
|
||||
const (
|
||||
// ECMA-48 Set Graphics Rendition
|
||||
// Note:
|
||||
// -- Constants leading with an underscore (e.g., _ANSI_xxx) are unsupported or reserved
|
||||
// -- Fonts could possibly be supported via SetCurrentConsoleFontEx
|
||||
// -- Windows does not expose the per-window cursor (i.e., caret) blink times
|
||||
ANSI_SGR_RESET = 0
|
||||
ANSI_SGR_BOLD = 1
|
||||
ANSI_SGR_DIM = 2
|
||||
_ANSI_SGR_ITALIC = 3
|
||||
ANSI_SGR_UNDERLINE = 4
|
||||
_ANSI_SGR_BLINKSLOW = 5
|
||||
_ANSI_SGR_BLINKFAST = 6
|
||||
ANSI_SGR_REVERSE = 7
|
||||
_ANSI_SGR_INVISIBLE = 8
|
||||
_ANSI_SGR_LINETHROUGH = 9
|
||||
_ANSI_SGR_FONT_00 = 10
|
||||
_ANSI_SGR_FONT_01 = 11
|
||||
_ANSI_SGR_FONT_02 = 12
|
||||
_ANSI_SGR_FONT_03 = 13
|
||||
_ANSI_SGR_FONT_04 = 14
|
||||
_ANSI_SGR_FONT_05 = 15
|
||||
_ANSI_SGR_FONT_06 = 16
|
||||
_ANSI_SGR_FONT_07 = 17
|
||||
_ANSI_SGR_FONT_08 = 18
|
||||
_ANSI_SGR_FONT_09 = 19
|
||||
_ANSI_SGR_FONT_10 = 20
|
||||
_ANSI_SGR_DOUBLEUNDERLINE = 21
|
||||
ANSI_SGR_BOLD_DIM_OFF = 22
|
||||
_ANSI_SGR_ITALIC_OFF = 23
|
||||
ANSI_SGR_UNDERLINE_OFF = 24
|
||||
_ANSI_SGR_BLINK_OFF = 25
|
||||
_ANSI_SGR_RESERVED_00 = 26
|
||||
ANSI_SGR_REVERSE_OFF = 27
|
||||
_ANSI_SGR_INVISIBLE_OFF = 28
|
||||
_ANSI_SGR_LINETHROUGH_OFF = 29
|
||||
ANSI_SGR_FOREGROUND_BLACK = 30
|
||||
ANSI_SGR_FOREGROUND_RED = 31
|
||||
ANSI_SGR_FOREGROUND_GREEN = 32
|
||||
ANSI_SGR_FOREGROUND_YELLOW = 33
|
||||
ANSI_SGR_FOREGROUND_BLUE = 34
|
||||
ANSI_SGR_FOREGROUND_MAGENTA = 35
|
||||
ANSI_SGR_FOREGROUND_CYAN = 36
|
||||
ANSI_SGR_FOREGROUND_WHITE = 37
|
||||
_ANSI_SGR_RESERVED_01 = 38
|
||||
ANSI_SGR_FOREGROUND_DEFAULT = 39
|
||||
ANSI_SGR_BACKGROUND_BLACK = 40
|
||||
ANSI_SGR_BACKGROUND_RED = 41
|
||||
ANSI_SGR_BACKGROUND_GREEN = 42
|
||||
ANSI_SGR_BACKGROUND_YELLOW = 43
|
||||
ANSI_SGR_BACKGROUND_BLUE = 44
|
||||
ANSI_SGR_BACKGROUND_MAGENTA = 45
|
||||
ANSI_SGR_BACKGROUND_CYAN = 46
|
||||
ANSI_SGR_BACKGROUND_WHITE = 47
|
||||
_ANSI_SGR_RESERVED_02 = 48
|
||||
ANSI_SGR_BACKGROUND_DEFAULT = 49
|
||||
// 50 - 65: Unsupported
|
||||
|
||||
ANSI_MAX_CMD_LENGTH = 4096
|
||||
|
||||
MAX_INPUT_EVENTS = 128
|
||||
DEFAULT_WIDTH = 80
|
||||
DEFAULT_HEIGHT = 24
|
||||
|
||||
ANSI_BEL = 0x07
|
||||
ANSI_BACKSPACE = 0x08
|
||||
ANSI_TAB = 0x09
|
||||
ANSI_LINE_FEED = 0x0A
|
||||
ANSI_VERTICAL_TAB = 0x0B
|
||||
ANSI_FORM_FEED = 0x0C
|
||||
ANSI_CARRIAGE_RETURN = 0x0D
|
||||
ANSI_ESCAPE_PRIMARY = 0x1B
|
||||
ANSI_ESCAPE_SECONDARY = 0x5B
|
||||
ANSI_OSC_STRING_ENTRY = 0x5D
|
||||
ANSI_COMMAND_FIRST = 0x40
|
||||
ANSI_COMMAND_LAST = 0x7E
|
||||
DCS_ENTRY = 0x90
|
||||
CSI_ENTRY = 0x9B
|
||||
OSC_STRING = 0x9D
|
||||
ANSI_PARAMETER_SEP = ";"
|
||||
ANSI_CMD_G0 = '('
|
||||
ANSI_CMD_G1 = ')'
|
||||
ANSI_CMD_G2 = '*'
|
||||
ANSI_CMD_G3 = '+'
|
||||
ANSI_CMD_DECPNM = '>'
|
||||
ANSI_CMD_DECPAM = '='
|
||||
ANSI_CMD_OSC = ']'
|
||||
ANSI_CMD_STR_TERM = '\\'
|
||||
|
||||
KEY_CONTROL_PARAM_2 = ";2"
|
||||
KEY_CONTROL_PARAM_3 = ";3"
|
||||
KEY_CONTROL_PARAM_4 = ";4"
|
||||
KEY_CONTROL_PARAM_5 = ";5"
|
||||
KEY_CONTROL_PARAM_6 = ";6"
|
||||
KEY_CONTROL_PARAM_7 = ";7"
|
||||
KEY_CONTROL_PARAM_8 = ";8"
|
||||
KEY_ESC_CSI = "\x1B["
|
||||
KEY_ESC_N = "\x1BN"
|
||||
KEY_ESC_O = "\x1BO"
|
||||
|
||||
FILL_CHARACTER = ' '
|
||||
)
|
||||
|
||||
func getByteRange(start byte, end byte) []byte {
|
||||
bytes := make([]byte, 0, 32)
|
||||
for i := start; i <= end; i++ {
|
||||
bytes = append(bytes, byte(i))
|
||||
}
|
||||
|
||||
return bytes
|
||||
}
|
||||
|
||||
var toGroundBytes = getToGroundBytes()
|
||||
var executors = getExecuteBytes()
|
||||
|
||||
// SPACE 20+A0 hex Always and everywhere a blank space
|
||||
// Intermediate 20-2F hex !"#$%&'()*+,-./
|
||||
var intermeds = getByteRange(0x20, 0x2F)
|
||||
|
||||
// Parameters 30-3F hex 0123456789:;<=>?
|
||||
// CSI Parameters 30-39, 3B hex 0123456789;
|
||||
var csiParams = getByteRange(0x30, 0x3F)
|
||||
|
||||
var csiCollectables = append(getByteRange(0x30, 0x39), getByteRange(0x3B, 0x3F)...)
|
||||
|
||||
// Uppercase 40-5F hex @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
|
||||
var upperCase = getByteRange(0x40, 0x5F)
|
||||
|
||||
// Lowercase 60-7E hex `abcdefghijlkmnopqrstuvwxyz{|}~
|
||||
var lowerCase = getByteRange(0x60, 0x7E)
|
||||
|
||||
// Alphabetics 40-7E hex (all of upper and lower case)
|
||||
var alphabetics = append(upperCase, lowerCase...)
|
||||
|
||||
var printables = getByteRange(0x20, 0x7F)
|
||||
|
||||
var escapeIntermediateToGroundBytes = getByteRange(0x30, 0x7E)
|
||||
var escapeToGroundBytes = getEscapeToGroundBytes()
|
||||
|
||||
// See http://www.vt100.net/emu/vt500_parser.png for description of the complex
|
||||
// byte ranges below
|
||||
|
||||
func getEscapeToGroundBytes() []byte {
|
||||
escapeToGroundBytes := getByteRange(0x30, 0x4F)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x51, 0x57)...)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, 0x59)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, 0x5A)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, 0x5C)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x60, 0x7E)...)
|
||||
return escapeToGroundBytes
|
||||
}
|
||||
|
||||
func getExecuteBytes() []byte {
|
||||
executeBytes := getByteRange(0x00, 0x17)
|
||||
executeBytes = append(executeBytes, 0x19)
|
||||
executeBytes = append(executeBytes, getByteRange(0x1C, 0x1F)...)
|
||||
return executeBytes
|
||||
}
|
||||
|
||||
func getToGroundBytes() []byte {
|
||||
groundBytes := []byte{0x18}
|
||||
groundBytes = append(groundBytes, 0x1A)
|
||||
groundBytes = append(groundBytes, getByteRange(0x80, 0x8F)...)
|
||||
groundBytes = append(groundBytes, getByteRange(0x91, 0x97)...)
|
||||
groundBytes = append(groundBytes, 0x99)
|
||||
groundBytes = append(groundBytes, 0x9A)
|
||||
groundBytes = append(groundBytes, 0x9C)
|
||||
return groundBytes
|
||||
}
|
||||
|
||||
// Delete 7F hex Always and everywhere ignored
|
||||
// C1 Control 80-9F hex 32 additional control characters
|
||||
// G1 Displayable A1-FE hex 94 additional displayable characters
|
||||
// Special A0+FF hex Same as SPACE and DELETE
|
||||
7
cli/vendor/github.com/Azure/go-ansiterm/context.go
generated
vendored
7
cli/vendor/github.com/Azure/go-ansiterm/context.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
type ansiContext struct {
|
||||
currentChar byte
|
||||
paramBuffer []byte
|
||||
interBuffer []byte
|
||||
}
|
||||
49
cli/vendor/github.com/Azure/go-ansiterm/csi_entry_state.go
generated
vendored
49
cli/vendor/github.com/Azure/go-ansiterm/csi_entry_state.go
generated
vendored
@@ -1,49 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
type csiEntryState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (csiState csiEntryState) Handle(b byte) (s state, e error) {
|
||||
logger.Infof("CsiEntry::Handle %#x", b)
|
||||
|
||||
nextState, err := csiState.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case sliceContains(alphabetics, b):
|
||||
return csiState.parser.ground, nil
|
||||
case sliceContains(csiCollectables, b):
|
||||
return csiState.parser.csiParam, nil
|
||||
case sliceContains(executors, b):
|
||||
return csiState, csiState.parser.execute()
|
||||
}
|
||||
|
||||
return csiState, nil
|
||||
}
|
||||
|
||||
func (csiState csiEntryState) Transition(s state) error {
|
||||
logger.Infof("CsiEntry::Transition %s --> %s", csiState.Name(), s.Name())
|
||||
csiState.baseState.Transition(s)
|
||||
|
||||
switch s {
|
||||
case csiState.parser.ground:
|
||||
return csiState.parser.csiDispatch()
|
||||
case csiState.parser.csiParam:
|
||||
switch {
|
||||
case sliceContains(csiParams, csiState.parser.context.currentChar):
|
||||
csiState.parser.collectParam()
|
||||
case sliceContains(intermeds, csiState.parser.context.currentChar):
|
||||
csiState.parser.collectInter()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (csiState csiEntryState) Enter() error {
|
||||
csiState.parser.clear()
|
||||
return nil
|
||||
}
|
||||
38
cli/vendor/github.com/Azure/go-ansiterm/csi_param_state.go
generated
vendored
38
cli/vendor/github.com/Azure/go-ansiterm/csi_param_state.go
generated
vendored
@@ -1,38 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
type csiParamState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (csiState csiParamState) Handle(b byte) (s state, e error) {
|
||||
logger.Infof("CsiParam::Handle %#x", b)
|
||||
|
||||
nextState, err := csiState.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case sliceContains(alphabetics, b):
|
||||
return csiState.parser.ground, nil
|
||||
case sliceContains(csiCollectables, b):
|
||||
csiState.parser.collectParam()
|
||||
return csiState, nil
|
||||
case sliceContains(executors, b):
|
||||
return csiState, csiState.parser.execute()
|
||||
}
|
||||
|
||||
return csiState, nil
|
||||
}
|
||||
|
||||
func (csiState csiParamState) Transition(s state) error {
|
||||
logger.Infof("CsiParam::Transition %s --> %s", csiState.Name(), s.Name())
|
||||
csiState.baseState.Transition(s)
|
||||
|
||||
switch s {
|
||||
case csiState.parser.ground:
|
||||
return csiState.parser.csiDispatch()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
36
cli/vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go
generated
vendored
36
cli/vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go
generated
vendored
@@ -1,36 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
type escapeIntermediateState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (escState escapeIntermediateState) Handle(b byte) (s state, e error) {
|
||||
logger.Infof("escapeIntermediateState::Handle %#x", b)
|
||||
nextState, err := escState.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case sliceContains(intermeds, b):
|
||||
return escState, escState.parser.collectInter()
|
||||
case sliceContains(executors, b):
|
||||
return escState, escState.parser.execute()
|
||||
case sliceContains(escapeIntermediateToGroundBytes, b):
|
||||
return escState.parser.ground, nil
|
||||
}
|
||||
|
||||
return escState, nil
|
||||
}
|
||||
|
||||
func (escState escapeIntermediateState) Transition(s state) error {
|
||||
logger.Infof("escapeIntermediateState::Transition %s --> %s", escState.Name(), s.Name())
|
||||
escState.baseState.Transition(s)
|
||||
|
||||
switch s {
|
||||
case escState.parser.ground:
|
||||
return escState.parser.escDispatch()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
47
cli/vendor/github.com/Azure/go-ansiterm/escape_state.go
generated
vendored
47
cli/vendor/github.com/Azure/go-ansiterm/escape_state.go
generated
vendored
@@ -1,47 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
type escapeState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (escState escapeState) Handle(b byte) (s state, e error) {
|
||||
logger.Infof("escapeState::Handle %#x", b)
|
||||
nextState, err := escState.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case b == ANSI_ESCAPE_SECONDARY:
|
||||
return escState.parser.csiEntry, nil
|
||||
case b == ANSI_OSC_STRING_ENTRY:
|
||||
return escState.parser.oscString, nil
|
||||
case sliceContains(executors, b):
|
||||
return escState, escState.parser.execute()
|
||||
case sliceContains(escapeToGroundBytes, b):
|
||||
return escState.parser.ground, nil
|
||||
case sliceContains(intermeds, b):
|
||||
return escState.parser.escapeIntermediate, nil
|
||||
}
|
||||
|
||||
return escState, nil
|
||||
}
|
||||
|
||||
func (escState escapeState) Transition(s state) error {
|
||||
logger.Infof("Escape::Transition %s --> %s", escState.Name(), s.Name())
|
||||
escState.baseState.Transition(s)
|
||||
|
||||
switch s {
|
||||
case escState.parser.ground:
|
||||
return escState.parser.escDispatch()
|
||||
case escState.parser.escapeIntermediate:
|
||||
return escState.parser.collectInter()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (escState escapeState) Enter() error {
|
||||
escState.parser.clear()
|
||||
return nil
|
||||
}
|
||||
90
cli/vendor/github.com/Azure/go-ansiterm/event_handler.go
generated
vendored
90
cli/vendor/github.com/Azure/go-ansiterm/event_handler.go
generated
vendored
@@ -1,90 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
type AnsiEventHandler interface {
|
||||
// Print
|
||||
Print(b byte) error
|
||||
|
||||
// Execute C0 commands
|
||||
Execute(b byte) error
|
||||
|
||||
// CUrsor Up
|
||||
CUU(int) error
|
||||
|
||||
// CUrsor Down
|
||||
CUD(int) error
|
||||
|
||||
// CUrsor Forward
|
||||
CUF(int) error
|
||||
|
||||
// CUrsor Backward
|
||||
CUB(int) error
|
||||
|
||||
// Cursor to Next Line
|
||||
CNL(int) error
|
||||
|
||||
// Cursor to Previous Line
|
||||
CPL(int) error
|
||||
|
||||
// Cursor Horizontal position Absolute
|
||||
CHA(int) error
|
||||
|
||||
// Vertical line Position Absolute
|
||||
VPA(int) error
|
||||
|
||||
// CUrsor Position
|
||||
CUP(int, int) error
|
||||
|
||||
// Horizontal and Vertical Position (depends on PUM)
|
||||
HVP(int, int) error
|
||||
|
||||
// Text Cursor Enable Mode
|
||||
DECTCEM(bool) error
|
||||
|
||||
// Origin Mode
|
||||
DECOM(bool) error
|
||||
|
||||
// 132 Column Mode
|
||||
DECCOLM(bool) error
|
||||
|
||||
// Erase in Display
|
||||
ED(int) error
|
||||
|
||||
// Erase in Line
|
||||
EL(int) error
|
||||
|
||||
// Insert Line
|
||||
IL(int) error
|
||||
|
||||
// Delete Line
|
||||
DL(int) error
|
||||
|
||||
// Insert Character
|
||||
ICH(int) error
|
||||
|
||||
// Delete Character
|
||||
DCH(int) error
|
||||
|
||||
// Set Graphics Rendition
|
||||
SGR([]int) error
|
||||
|
||||
// Pan Down
|
||||
SU(int) error
|
||||
|
||||
// Pan Up
|
||||
SD(int) error
|
||||
|
||||
// Device Attributes
|
||||
DA([]string) error
|
||||
|
||||
// Set Top and Bottom Margins
|
||||
DECSTBM(int, int) error
|
||||
|
||||
// Index
|
||||
IND() error
|
||||
|
||||
// Reverse Index
|
||||
RI() error
|
||||
|
||||
// Flush updates from previous commands
|
||||
Flush() error
|
||||
}
|
||||
24
cli/vendor/github.com/Azure/go-ansiterm/ground_state.go
generated
vendored
24
cli/vendor/github.com/Azure/go-ansiterm/ground_state.go
generated
vendored
@@ -1,24 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
type groundState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (gs groundState) Handle(b byte) (s state, e error) {
|
||||
gs.parser.context.currentChar = b
|
||||
|
||||
nextState, err := gs.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case sliceContains(printables, b):
|
||||
return gs, gs.parser.print()
|
||||
|
||||
case sliceContains(executors, b):
|
||||
return gs, gs.parser.execute()
|
||||
}
|
||||
|
||||
return gs, nil
|
||||
}
|
||||
31
cli/vendor/github.com/Azure/go-ansiterm/osc_string_state.go
generated
vendored
31
cli/vendor/github.com/Azure/go-ansiterm/osc_string_state.go
generated
vendored
@@ -1,31 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
type oscStringState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (oscState oscStringState) Handle(b byte) (s state, e error) {
|
||||
logger.Infof("OscString::Handle %#x", b)
|
||||
nextState, err := oscState.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case isOscStringTerminator(b):
|
||||
return oscState.parser.ground, nil
|
||||
}
|
||||
|
||||
return oscState, nil
|
||||
}
|
||||
|
||||
// See below for OSC string terminators for linux
|
||||
// http://man7.org/linux/man-pages/man4/console_codes.4.html
|
||||
func isOscStringTerminator(b byte) bool {
|
||||
|
||||
if b == ANSI_BEL || b == 0x5C {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
136
cli/vendor/github.com/Azure/go-ansiterm/parser.go
generated
vendored
136
cli/vendor/github.com/Azure/go-ansiterm/parser.go
generated
vendored
@@ -1,136 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
var logger *logrus.Logger
|
||||
|
||||
type AnsiParser struct {
|
||||
currState state
|
||||
eventHandler AnsiEventHandler
|
||||
context *ansiContext
|
||||
csiEntry state
|
||||
csiParam state
|
||||
dcsEntry state
|
||||
escape state
|
||||
escapeIntermediate state
|
||||
error state
|
||||
ground state
|
||||
oscString state
|
||||
stateMap []state
|
||||
}
|
||||
|
||||
func CreateParser(initialState string, evtHandler AnsiEventHandler) *AnsiParser {
|
||||
logFile := ioutil.Discard
|
||||
|
||||
if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" {
|
||||
logFile, _ = os.Create("ansiParser.log")
|
||||
}
|
||||
|
||||
logger = &logrus.Logger{
|
||||
Out: logFile,
|
||||
Formatter: new(logrus.TextFormatter),
|
||||
Level: logrus.InfoLevel,
|
||||
}
|
||||
|
||||
parser := &AnsiParser{
|
||||
eventHandler: evtHandler,
|
||||
context: &ansiContext{},
|
||||
}
|
||||
|
||||
parser.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: parser}}
|
||||
parser.csiParam = csiParamState{baseState{name: "CsiParam", parser: parser}}
|
||||
parser.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: parser}}
|
||||
parser.escape = escapeState{baseState{name: "Escape", parser: parser}}
|
||||
parser.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: parser}}
|
||||
parser.error = errorState{baseState{name: "Error", parser: parser}}
|
||||
parser.ground = groundState{baseState{name: "Ground", parser: parser}}
|
||||
parser.oscString = oscStringState{baseState{name: "OscString", parser: parser}}
|
||||
|
||||
parser.stateMap = []state{
|
||||
parser.csiEntry,
|
||||
parser.csiParam,
|
||||
parser.dcsEntry,
|
||||
parser.escape,
|
||||
parser.escapeIntermediate,
|
||||
parser.error,
|
||||
parser.ground,
|
||||
parser.oscString,
|
||||
}
|
||||
|
||||
parser.currState = getState(initialState, parser.stateMap)
|
||||
|
||||
logger.Infof("CreateParser: parser %p", parser)
|
||||
return parser
|
||||
}
|
||||
|
||||
func getState(name string, states []state) state {
|
||||
for _, el := range states {
|
||||
if el.Name() == name {
|
||||
return el
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) Parse(bytes []byte) (int, error) {
|
||||
for i, b := range bytes {
|
||||
if err := ap.handle(b); err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
|
||||
return len(bytes), ap.eventHandler.Flush()
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) handle(b byte) error {
|
||||
ap.context.currentChar = b
|
||||
newState, err := ap.currState.Handle(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if newState == nil {
|
||||
logger.Warning("newState is nil")
|
||||
return errors.New("New state of 'nil' is invalid.")
|
||||
}
|
||||
|
||||
if newState != ap.currState {
|
||||
if err := ap.changeState(newState); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) changeState(newState state) error {
|
||||
logger.Infof("ChangeState %s --> %s", ap.currState.Name(), newState.Name())
|
||||
|
||||
// Exit old state
|
||||
if err := ap.currState.Exit(); err != nil {
|
||||
logger.Infof("Exit state '%s' failed with : '%v'", ap.currState.Name(), err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Perform transition action
|
||||
if err := ap.currState.Transition(newState); err != nil {
|
||||
logger.Infof("Transition from '%s' to '%s' failed with: '%v'", ap.currState.Name(), newState.Name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Enter new state
|
||||
if err := newState.Enter(); err != nil {
|
||||
logger.Infof("Enter state '%s' failed with: '%v'", newState.Name(), err)
|
||||
return err
|
||||
}
|
||||
|
||||
ap.currState = newState
|
||||
return nil
|
||||
}
|
||||
103
cli/vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go
generated
vendored
103
cli/vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go
generated
vendored
@@ -1,103 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func parseParams(bytes []byte) ([]string, error) {
|
||||
paramBuff := make([]byte, 0, 0)
|
||||
params := []string{}
|
||||
|
||||
for _, v := range bytes {
|
||||
if v == ';' {
|
||||
if len(paramBuff) > 0 {
|
||||
// Completed parameter, append it to the list
|
||||
s := string(paramBuff)
|
||||
params = append(params, s)
|
||||
paramBuff = make([]byte, 0, 0)
|
||||
}
|
||||
} else {
|
||||
paramBuff = append(paramBuff, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Last parameter may not be terminated with ';'
|
||||
if len(paramBuff) > 0 {
|
||||
s := string(paramBuff)
|
||||
params = append(params, s)
|
||||
}
|
||||
|
||||
logger.Infof("Parsed params: %v with length: %d", params, len(params))
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func parseCmd(context ansiContext) (string, error) {
|
||||
return string(context.currentChar), nil
|
||||
}
|
||||
|
||||
func getInt(params []string, dflt int) int {
|
||||
i := getInts(params, 1, dflt)[0]
|
||||
logger.Infof("getInt: %v", i)
|
||||
return i
|
||||
}
|
||||
|
||||
func getInts(params []string, minCount int, dflt int) []int {
|
||||
ints := []int{}
|
||||
|
||||
for _, v := range params {
|
||||
i, _ := strconv.Atoi(v)
|
||||
// Zero is mapped to the default value in VT100.
|
||||
if i == 0 {
|
||||
i = dflt
|
||||
}
|
||||
ints = append(ints, i)
|
||||
}
|
||||
|
||||
if len(ints) < minCount {
|
||||
remaining := minCount - len(ints)
|
||||
for i := 0; i < remaining; i++ {
|
||||
ints = append(ints, dflt)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Infof("getInts: %v", ints)
|
||||
|
||||
return ints
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) modeDispatch(param string, set bool) error {
|
||||
switch param {
|
||||
case "?3":
|
||||
return ap.eventHandler.DECCOLM(set)
|
||||
case "?6":
|
||||
return ap.eventHandler.DECOM(set)
|
||||
case "?25":
|
||||
return ap.eventHandler.DECTCEM(set)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) hDispatch(params []string) error {
|
||||
if len(params) == 1 {
|
||||
return ap.modeDispatch(params[0], true)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) lDispatch(params []string) error {
|
||||
if len(params) == 1 {
|
||||
return ap.modeDispatch(params[0], false)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEraseParam(params []string) int {
|
||||
param := getInt(params, 0)
|
||||
if param < 0 || 3 < param {
|
||||
param = 0
|
||||
}
|
||||
|
||||
return param
|
||||
}
|
||||
122
cli/vendor/github.com/Azure/go-ansiterm/parser_actions.go
generated
vendored
122
cli/vendor/github.com/Azure/go-ansiterm/parser_actions.go
generated
vendored
@@ -1,122 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (ap *AnsiParser) collectParam() error {
|
||||
currChar := ap.context.currentChar
|
||||
logger.Infof("collectParam %#x", currChar)
|
||||
ap.context.paramBuffer = append(ap.context.paramBuffer, currChar)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) collectInter() error {
|
||||
currChar := ap.context.currentChar
|
||||
logger.Infof("collectInter %#x", currChar)
|
||||
ap.context.paramBuffer = append(ap.context.interBuffer, currChar)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) escDispatch() error {
|
||||
cmd, _ := parseCmd(*ap.context)
|
||||
intermeds := ap.context.interBuffer
|
||||
logger.Infof("escDispatch currentChar: %#x", ap.context.currentChar)
|
||||
logger.Infof("escDispatch: %v(%v)", cmd, intermeds)
|
||||
|
||||
switch cmd {
|
||||
case "D": // IND
|
||||
return ap.eventHandler.IND()
|
||||
case "E": // NEL, equivalent to CRLF
|
||||
err := ap.eventHandler.Execute(ANSI_CARRIAGE_RETURN)
|
||||
if err == nil {
|
||||
err = ap.eventHandler.Execute(ANSI_LINE_FEED)
|
||||
}
|
||||
return err
|
||||
case "M": // RI
|
||||
return ap.eventHandler.RI()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) csiDispatch() error {
|
||||
cmd, _ := parseCmd(*ap.context)
|
||||
params, _ := parseParams(ap.context.paramBuffer)
|
||||
|
||||
logger.Infof("csiDispatch: %v(%v)", cmd, params)
|
||||
|
||||
switch cmd {
|
||||
case "@":
|
||||
return ap.eventHandler.ICH(getInt(params, 1))
|
||||
case "A":
|
||||
return ap.eventHandler.CUU(getInt(params, 1))
|
||||
case "B":
|
||||
return ap.eventHandler.CUD(getInt(params, 1))
|
||||
case "C":
|
||||
return ap.eventHandler.CUF(getInt(params, 1))
|
||||
case "D":
|
||||
return ap.eventHandler.CUB(getInt(params, 1))
|
||||
case "E":
|
||||
return ap.eventHandler.CNL(getInt(params, 1))
|
||||
case "F":
|
||||
return ap.eventHandler.CPL(getInt(params, 1))
|
||||
case "G":
|
||||
return ap.eventHandler.CHA(getInt(params, 1))
|
||||
case "H":
|
||||
ints := getInts(params, 2, 1)
|
||||
x, y := ints[0], ints[1]
|
||||
return ap.eventHandler.CUP(x, y)
|
||||
case "J":
|
||||
param := getEraseParam(params)
|
||||
return ap.eventHandler.ED(param)
|
||||
case "K":
|
||||
param := getEraseParam(params)
|
||||
return ap.eventHandler.EL(param)
|
||||
case "L":
|
||||
return ap.eventHandler.IL(getInt(params, 1))
|
||||
case "M":
|
||||
return ap.eventHandler.DL(getInt(params, 1))
|
||||
case "P":
|
||||
return ap.eventHandler.DCH(getInt(params, 1))
|
||||
case "S":
|
||||
return ap.eventHandler.SU(getInt(params, 1))
|
||||
case "T":
|
||||
return ap.eventHandler.SD(getInt(params, 1))
|
||||
case "c":
|
||||
return ap.eventHandler.DA(params)
|
||||
case "d":
|
||||
return ap.eventHandler.VPA(getInt(params, 1))
|
||||
case "f":
|
||||
ints := getInts(params, 2, 1)
|
||||
x, y := ints[0], ints[1]
|
||||
return ap.eventHandler.HVP(x, y)
|
||||
case "h":
|
||||
return ap.hDispatch(params)
|
||||
case "l":
|
||||
return ap.lDispatch(params)
|
||||
case "m":
|
||||
return ap.eventHandler.SGR(getInts(params, 1, 0))
|
||||
case "r":
|
||||
ints := getInts(params, 2, 1)
|
||||
top, bottom := ints[0], ints[1]
|
||||
return ap.eventHandler.DECSTBM(top, bottom)
|
||||
default:
|
||||
logger.Errorf(fmt.Sprintf("Unsupported CSI command: '%s', with full context: %v", cmd, ap.context))
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) print() error {
|
||||
return ap.eventHandler.Print(ap.context.currentChar)
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) clear() error {
|
||||
ap.context = &ansiContext{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ap *AnsiParser) execute() error {
|
||||
return ap.eventHandler.Execute(ap.context.currentChar)
|
||||
}
|
||||
141
cli/vendor/github.com/Azure/go-ansiterm/parser_test.go
generated
vendored
141
cli/vendor/github.com/Azure/go-ansiterm/parser_test.go
generated
vendored
@@ -1,141 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStateTransitions(t *testing.T) {
|
||||
stateTransitionHelper(t, "CsiEntry", "Ground", alphabetics)
|
||||
stateTransitionHelper(t, "CsiEntry", "CsiParam", csiCollectables)
|
||||
stateTransitionHelper(t, "Escape", "CsiEntry", []byte{ANSI_ESCAPE_SECONDARY})
|
||||
stateTransitionHelper(t, "Escape", "OscString", []byte{0x5D})
|
||||
stateTransitionHelper(t, "Escape", "Ground", escapeToGroundBytes)
|
||||
stateTransitionHelper(t, "Escape", "EscapeIntermediate", intermeds)
|
||||
stateTransitionHelper(t, "EscapeIntermediate", "EscapeIntermediate", intermeds)
|
||||
stateTransitionHelper(t, "EscapeIntermediate", "EscapeIntermediate", executors)
|
||||
stateTransitionHelper(t, "EscapeIntermediate", "Ground", escapeIntermediateToGroundBytes)
|
||||
stateTransitionHelper(t, "OscString", "Ground", []byte{ANSI_BEL})
|
||||
stateTransitionHelper(t, "OscString", "Ground", []byte{0x5C})
|
||||
stateTransitionHelper(t, "Ground", "Ground", executors)
|
||||
}
|
||||
|
||||
func TestAnyToX(t *testing.T) {
|
||||
anyToXHelper(t, []byte{ANSI_ESCAPE_PRIMARY}, "Escape")
|
||||
anyToXHelper(t, []byte{DCS_ENTRY}, "DcsEntry")
|
||||
anyToXHelper(t, []byte{OSC_STRING}, "OscString")
|
||||
anyToXHelper(t, []byte{CSI_ENTRY}, "CsiEntry")
|
||||
anyToXHelper(t, toGroundBytes, "Ground")
|
||||
}
|
||||
|
||||
func TestCollectCsiParams(t *testing.T) {
|
||||
parser, _ := createTestParser("CsiEntry")
|
||||
parser.Parse(csiCollectables)
|
||||
|
||||
buffer := parser.context.paramBuffer
|
||||
bufferCount := len(buffer)
|
||||
|
||||
if bufferCount != len(csiCollectables) {
|
||||
t.Errorf("Buffer: %v", buffer)
|
||||
t.Errorf("CsiParams: %v", csiCollectables)
|
||||
t.Errorf("Buffer count failure: %d != %d", bufferCount, len(csiParams))
|
||||
return
|
||||
}
|
||||
|
||||
for i, v := range csiCollectables {
|
||||
if v != buffer[i] {
|
||||
t.Errorf("Buffer: %v", buffer)
|
||||
t.Errorf("CsiParams: %v", csiParams)
|
||||
t.Errorf("Mismatch at buffer[%d] = %d", i, buffer[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseParams(t *testing.T) {
|
||||
parseParamsHelper(t, []byte{}, []string{})
|
||||
parseParamsHelper(t, []byte{';'}, []string{})
|
||||
parseParamsHelper(t, []byte{';', ';'}, []string{})
|
||||
parseParamsHelper(t, []byte{'7'}, []string{"7"})
|
||||
parseParamsHelper(t, []byte{'7', ';'}, []string{"7"})
|
||||
parseParamsHelper(t, []byte{'7', ';', ';'}, []string{"7"})
|
||||
parseParamsHelper(t, []byte{'7', ';', ';', '8'}, []string{"7", "8"})
|
||||
parseParamsHelper(t, []byte{'7', ';', '8', ';'}, []string{"7", "8"})
|
||||
parseParamsHelper(t, []byte{'7', ';', ';', '8', ';', ';'}, []string{"7", "8"})
|
||||
parseParamsHelper(t, []byte{'7', '8'}, []string{"78"})
|
||||
parseParamsHelper(t, []byte{'7', '8', ';'}, []string{"78"})
|
||||
parseParamsHelper(t, []byte{'7', '8', ';', '9', '0'}, []string{"78", "90"})
|
||||
parseParamsHelper(t, []byte{'7', '8', ';', ';', '9', '0'}, []string{"78", "90"})
|
||||
parseParamsHelper(t, []byte{'7', '8', ';', '9', '0', ';'}, []string{"78", "90"})
|
||||
parseParamsHelper(t, []byte{'7', '8', ';', '9', '0', ';', ';'}, []string{"78", "90"})
|
||||
}
|
||||
|
||||
func TestCursor(t *testing.T) {
|
||||
cursorSingleParamHelper(t, 'A', "CUU")
|
||||
cursorSingleParamHelper(t, 'B', "CUD")
|
||||
cursorSingleParamHelper(t, 'C', "CUF")
|
||||
cursorSingleParamHelper(t, 'D', "CUB")
|
||||
cursorSingleParamHelper(t, 'E', "CNL")
|
||||
cursorSingleParamHelper(t, 'F', "CPL")
|
||||
cursorSingleParamHelper(t, 'G', "CHA")
|
||||
cursorTwoParamHelper(t, 'H', "CUP")
|
||||
cursorTwoParamHelper(t, 'f', "HVP")
|
||||
funcCallParamHelper(t, []byte{'?', '2', '5', 'h'}, "CsiEntry", "Ground", []string{"DECTCEM([true])"})
|
||||
funcCallParamHelper(t, []byte{'?', '2', '5', 'l'}, "CsiEntry", "Ground", []string{"DECTCEM([false])"})
|
||||
}
|
||||
|
||||
func TestErase(t *testing.T) {
|
||||
// Erase in Display
|
||||
eraseHelper(t, 'J', "ED")
|
||||
|
||||
// Erase in Line
|
||||
eraseHelper(t, 'K', "EL")
|
||||
}
|
||||
|
||||
func TestSelectGraphicRendition(t *testing.T) {
|
||||
funcCallParamHelper(t, []byte{'m'}, "CsiEntry", "Ground", []string{"SGR([0])"})
|
||||
funcCallParamHelper(t, []byte{'0', 'm'}, "CsiEntry", "Ground", []string{"SGR([0])"})
|
||||
funcCallParamHelper(t, []byte{'0', ';', '1', 'm'}, "CsiEntry", "Ground", []string{"SGR([0 1])"})
|
||||
funcCallParamHelper(t, []byte{'0', ';', '1', ';', '2', 'm'}, "CsiEntry", "Ground", []string{"SGR([0 1 2])"})
|
||||
}
|
||||
|
||||
func TestScroll(t *testing.T) {
|
||||
scrollHelper(t, 'S', "SU")
|
||||
scrollHelper(t, 'T', "SD")
|
||||
}
|
||||
|
||||
func TestPrint(t *testing.T) {
|
||||
parser, evtHandler := createTestParser("Ground")
|
||||
parser.Parse(printables)
|
||||
validateState(t, parser.currState, "Ground")
|
||||
|
||||
for i, v := range printables {
|
||||
expectedCall := fmt.Sprintf("Print([%s])", string(v))
|
||||
actualCall := evtHandler.FunctionCalls[i]
|
||||
if actualCall != expectedCall {
|
||||
t.Errorf("Actual != Expected: %v != %v at %d", actualCall, expectedCall, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClear(t *testing.T) {
|
||||
p, _ := createTestParser("Ground")
|
||||
fillContext(p.context)
|
||||
p.clear()
|
||||
validateEmptyContext(t, p.context)
|
||||
}
|
||||
|
||||
func TestClearOnStateChange(t *testing.T) {
|
||||
clearOnStateChangeHelper(t, "Ground", "Escape", []byte{ANSI_ESCAPE_PRIMARY})
|
||||
clearOnStateChangeHelper(t, "Ground", "CsiEntry", []byte{CSI_ENTRY})
|
||||
}
|
||||
|
||||
func TestC0(t *testing.T) {
|
||||
expectedCall := "Execute([" + string(ANSI_LINE_FEED) + "])"
|
||||
c0Helper(t, []byte{ANSI_LINE_FEED}, "Ground", []string{expectedCall})
|
||||
expectedCall = "Execute([" + string(ANSI_CARRIAGE_RETURN) + "])"
|
||||
c0Helper(t, []byte{ANSI_CARRIAGE_RETURN}, "Ground", []string{expectedCall})
|
||||
}
|
||||
|
||||
func TestEscDispatch(t *testing.T) {
|
||||
funcCallParamHelper(t, []byte{'M'}, "Escape", "Ground", []string{"RI([])"})
|
||||
}
|
||||
114
cli/vendor/github.com/Azure/go-ansiterm/parser_test_helpers_test.go
generated
vendored
114
cli/vendor/github.com/Azure/go-ansiterm/parser_test_helpers_test.go
generated
vendored
@@ -1,114 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getStateNames() []string {
|
||||
parser, _ := createTestParser("Ground")
|
||||
|
||||
stateNames := []string{}
|
||||
for _, state := range parser.stateMap {
|
||||
stateNames = append(stateNames, state.Name())
|
||||
}
|
||||
|
||||
return stateNames
|
||||
}
|
||||
|
||||
func stateTransitionHelper(t *testing.T, start string, end string, bytes []byte) {
|
||||
for _, b := range bytes {
|
||||
bytes := []byte{byte(b)}
|
||||
parser, _ := createTestParser(start)
|
||||
parser.Parse(bytes)
|
||||
validateState(t, parser.currState, end)
|
||||
}
|
||||
}
|
||||
|
||||
func anyToXHelper(t *testing.T, bytes []byte, expectedState string) {
|
||||
for _, s := range getStateNames() {
|
||||
stateTransitionHelper(t, s, expectedState, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
func funcCallParamHelper(t *testing.T, bytes []byte, start string, expected string, expectedCalls []string) {
|
||||
parser, evtHandler := createTestParser(start)
|
||||
parser.Parse(bytes)
|
||||
validateState(t, parser.currState, expected)
|
||||
validateFuncCalls(t, evtHandler.FunctionCalls, expectedCalls)
|
||||
}
|
||||
|
||||
func parseParamsHelper(t *testing.T, bytes []byte, expectedParams []string) {
|
||||
params, err := parseParams(bytes)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Parameter parse error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(params) != len(expectedParams) {
|
||||
t.Errorf("Parsed parameters: %v", params)
|
||||
t.Errorf("Expected parameters: %v", expectedParams)
|
||||
t.Errorf("Parameter length failure: %d != %d", len(params), len(expectedParams))
|
||||
return
|
||||
}
|
||||
|
||||
for i, v := range expectedParams {
|
||||
if v != params[i] {
|
||||
t.Errorf("Parsed parameters: %v", params)
|
||||
t.Errorf("Expected parameters: %v", expectedParams)
|
||||
t.Errorf("Parameter parse failure: %s != %s at position %d", v, params[i], i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cursorSingleParamHelper(t *testing.T, command byte, funcName string) {
|
||||
funcCallParamHelper(t, []byte{command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'0', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'2', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'2', '3', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([23])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'2', ';', '3', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'2', ';', '3', ';', '4', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2])", funcName)})
|
||||
}
|
||||
|
||||
func cursorTwoParamHelper(t *testing.T, command byte, funcName string) {
|
||||
funcCallParamHelper(t, []byte{command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1 1])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'0', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1 1])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'2', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2 1])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'2', '3', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([23 1])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'2', ';', '3', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2 3])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'2', ';', '3', ';', '4', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2 3])", funcName)})
|
||||
}
|
||||
|
||||
func eraseHelper(t *testing.T, command byte, funcName string) {
|
||||
funcCallParamHelper(t, []byte{command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([0])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'0', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([0])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'1', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'2', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'3', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([3])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'4', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([0])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'1', ';', '2', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
|
||||
}
|
||||
|
||||
func scrollHelper(t *testing.T, command byte, funcName string) {
|
||||
funcCallParamHelper(t, []byte{command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'0', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'1', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'5', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([5])", funcName)})
|
||||
funcCallParamHelper(t, []byte{'4', ';', '6', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([4])", funcName)})
|
||||
}
|
||||
|
||||
func clearOnStateChangeHelper(t *testing.T, start string, end string, bytes []byte) {
|
||||
p, _ := createTestParser(start)
|
||||
fillContext(p.context)
|
||||
p.Parse(bytes)
|
||||
validateState(t, p.currState, end)
|
||||
validateEmptyContext(t, p.context)
|
||||
}
|
||||
|
||||
func c0Helper(t *testing.T, bytes []byte, expectedState string, expectedCalls []string) {
|
||||
parser, evtHandler := createTestParser("Ground")
|
||||
parser.Parse(bytes)
|
||||
validateState(t, parser.currState, expectedState)
|
||||
validateFuncCalls(t, evtHandler.FunctionCalls, expectedCalls)
|
||||
}
|
||||
66
cli/vendor/github.com/Azure/go-ansiterm/parser_test_utilities_test.go
generated
vendored
66
cli/vendor/github.com/Azure/go-ansiterm/parser_test_utilities_test.go
generated
vendored
@@ -1,66 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func createTestParser(s string) (*AnsiParser, *TestAnsiEventHandler) {
|
||||
evtHandler := CreateTestAnsiEventHandler()
|
||||
parser := CreateParser(s, evtHandler)
|
||||
|
||||
return parser, evtHandler
|
||||
}
|
||||
|
||||
func validateState(t *testing.T, actualState state, expectedStateName string) {
|
||||
actualName := "Nil"
|
||||
|
||||
if actualState != nil {
|
||||
actualName = actualState.Name()
|
||||
}
|
||||
|
||||
if actualName != expectedStateName {
|
||||
t.Errorf("Invalid state: '%s' != '%s'", actualName, expectedStateName)
|
||||
}
|
||||
}
|
||||
|
||||
func validateFuncCalls(t *testing.T, actualCalls []string, expectedCalls []string) {
|
||||
actualCount := len(actualCalls)
|
||||
expectedCount := len(expectedCalls)
|
||||
|
||||
if actualCount != expectedCount {
|
||||
t.Errorf("Actual calls: %v", actualCalls)
|
||||
t.Errorf("Expected calls: %v", expectedCalls)
|
||||
t.Errorf("Call count error: %d != %d", actualCount, expectedCount)
|
||||
return
|
||||
}
|
||||
|
||||
for i, v := range actualCalls {
|
||||
if v != expectedCalls[i] {
|
||||
t.Errorf("Actual calls: %v", actualCalls)
|
||||
t.Errorf("Expected calls: %v", expectedCalls)
|
||||
t.Errorf("Mismatched calls: %s != %s with lengths %d and %d", v, expectedCalls[i], len(v), len(expectedCalls[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fillContext(context *ansiContext) {
|
||||
context.currentChar = 'A'
|
||||
context.paramBuffer = []byte{'C', 'D', 'E'}
|
||||
context.interBuffer = []byte{'F', 'G', 'H'}
|
||||
}
|
||||
|
||||
func validateEmptyContext(t *testing.T, context *ansiContext) {
|
||||
var expectedCurrChar byte = 0x0
|
||||
if context.currentChar != expectedCurrChar {
|
||||
t.Errorf("Currentchar mismatch '%#x' != '%#x'", context.currentChar, expectedCurrChar)
|
||||
}
|
||||
|
||||
if len(context.paramBuffer) != 0 {
|
||||
t.Errorf("Non-empty parameter buffer: %v", context.paramBuffer)
|
||||
}
|
||||
|
||||
if len(context.paramBuffer) != 0 {
|
||||
t.Errorf("Non-empty intermediate buffer: %v", context.interBuffer)
|
||||
}
|
||||
|
||||
}
|
||||
71
cli/vendor/github.com/Azure/go-ansiterm/states.go
generated
vendored
71
cli/vendor/github.com/Azure/go-ansiterm/states.go
generated
vendored
@@ -1,71 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
type stateID int
|
||||
|
||||
type state interface {
|
||||
Enter() error
|
||||
Exit() error
|
||||
Handle(byte) (state, error)
|
||||
Name() string
|
||||
Transition(state) error
|
||||
}
|
||||
|
||||
type baseState struct {
|
||||
name string
|
||||
parser *AnsiParser
|
||||
}
|
||||
|
||||
func (base baseState) Enter() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (base baseState) Exit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (base baseState) Handle(b byte) (s state, e error) {
|
||||
|
||||
switch {
|
||||
case b == CSI_ENTRY:
|
||||
return base.parser.csiEntry, nil
|
||||
case b == DCS_ENTRY:
|
||||
return base.parser.dcsEntry, nil
|
||||
case b == ANSI_ESCAPE_PRIMARY:
|
||||
return base.parser.escape, nil
|
||||
case b == OSC_STRING:
|
||||
return base.parser.oscString, nil
|
||||
case sliceContains(toGroundBytes, b):
|
||||
return base.parser.ground, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (base baseState) Name() string {
|
||||
return base.name
|
||||
}
|
||||
|
||||
func (base baseState) Transition(s state) error {
|
||||
if s == base.parser.ground {
|
||||
execBytes := []byte{0x18}
|
||||
execBytes = append(execBytes, 0x1A)
|
||||
execBytes = append(execBytes, getByteRange(0x80, 0x8F)...)
|
||||
execBytes = append(execBytes, getByteRange(0x91, 0x97)...)
|
||||
execBytes = append(execBytes, 0x99)
|
||||
execBytes = append(execBytes, 0x9A)
|
||||
|
||||
if sliceContains(execBytes, base.parser.context.currentChar) {
|
||||
return base.parser.execute()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type dcsEntryState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
type errorState struct {
|
||||
baseState
|
||||
}
|
||||
173
cli/vendor/github.com/Azure/go-ansiterm/test_event_handler_test.go
generated
vendored
173
cli/vendor/github.com/Azure/go-ansiterm/test_event_handler_test.go
generated
vendored
@@ -1,173 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type TestAnsiEventHandler struct {
|
||||
FunctionCalls []string
|
||||
}
|
||||
|
||||
func CreateTestAnsiEventHandler() *TestAnsiEventHandler {
|
||||
evtHandler := TestAnsiEventHandler{}
|
||||
evtHandler.FunctionCalls = make([]string, 0)
|
||||
return &evtHandler
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) recordCall(call string, params []string) {
|
||||
s := fmt.Sprintf("%s(%v)", call, params)
|
||||
h.FunctionCalls = append(h.FunctionCalls, s)
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) Print(b byte) error {
|
||||
h.recordCall("Print", []string{string(b)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) Execute(b byte) error {
|
||||
h.recordCall("Execute", []string{string(b)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) CUU(param int) error {
|
||||
h.recordCall("CUU", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) CUD(param int) error {
|
||||
h.recordCall("CUD", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) CUF(param int) error {
|
||||
h.recordCall("CUF", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) CUB(param int) error {
|
||||
h.recordCall("CUB", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) CNL(param int) error {
|
||||
h.recordCall("CNL", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) CPL(param int) error {
|
||||
h.recordCall("CPL", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) CHA(param int) error {
|
||||
h.recordCall("CHA", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) VPA(param int) error {
|
||||
h.recordCall("VPA", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) CUP(x int, y int) error {
|
||||
xS, yS := strconv.Itoa(x), strconv.Itoa(y)
|
||||
h.recordCall("CUP", []string{xS, yS})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) HVP(x int, y int) error {
|
||||
xS, yS := strconv.Itoa(x), strconv.Itoa(y)
|
||||
h.recordCall("HVP", []string{xS, yS})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) DECTCEM(visible bool) error {
|
||||
h.recordCall("DECTCEM", []string{strconv.FormatBool(visible)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) DECOM(visible bool) error {
|
||||
h.recordCall("DECOM", []string{strconv.FormatBool(visible)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) DECCOLM(use132 bool) error {
|
||||
h.recordCall("DECOLM", []string{strconv.FormatBool(use132)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) ED(param int) error {
|
||||
h.recordCall("ED", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) EL(param int) error {
|
||||
h.recordCall("EL", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) IL(param int) error {
|
||||
h.recordCall("IL", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) DL(param int) error {
|
||||
h.recordCall("DL", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) ICH(param int) error {
|
||||
h.recordCall("ICH", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) DCH(param int) error {
|
||||
h.recordCall("DCH", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) SGR(params []int) error {
|
||||
strings := []string{}
|
||||
for _, v := range params {
|
||||
strings = append(strings, strconv.Itoa(v))
|
||||
}
|
||||
|
||||
h.recordCall("SGR", strings)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) SU(param int) error {
|
||||
h.recordCall("SU", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) SD(param int) error {
|
||||
h.recordCall("SD", []string{strconv.Itoa(param)})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) DA(params []string) error {
|
||||
h.recordCall("DA", params)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) DECSTBM(top int, bottom int) error {
|
||||
topS, bottomS := strconv.Itoa(top), strconv.Itoa(bottom)
|
||||
h.recordCall("DECSTBM", []string{topS, bottomS})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) RI() error {
|
||||
h.recordCall("RI", nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) IND() error {
|
||||
h.recordCall("IND", nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TestAnsiEventHandler) Flush() error {
|
||||
return nil
|
||||
}
|
||||
21
cli/vendor/github.com/Azure/go-ansiterm/utilities.go
generated
vendored
21
cli/vendor/github.com/Azure/go-ansiterm/utilities.go
generated
vendored
@@ -1,21 +0,0 @@
|
||||
package ansiterm
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func sliceContains(bytes []byte, b byte) bool {
|
||||
for _, v := range bytes {
|
||||
if v == b {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func convertBytesToInteger(bytes []byte) int {
|
||||
s := string(bytes)
|
||||
i, _ := strconv.Atoi(s)
|
||||
return i
|
||||
}
|
||||
182
cli/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go
generated
vendored
182
cli/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go
generated
vendored
@@ -1,182 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/Azure/go-ansiterm"
|
||||
)
|
||||
|
||||
// Windows keyboard constants
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx.
|
||||
const (
|
||||
VK_PRIOR = 0x21 // PAGE UP key
|
||||
VK_NEXT = 0x22 // PAGE DOWN key
|
||||
VK_END = 0x23 // END key
|
||||
VK_HOME = 0x24 // HOME key
|
||||
VK_LEFT = 0x25 // LEFT ARROW key
|
||||
VK_UP = 0x26 // UP ARROW key
|
||||
VK_RIGHT = 0x27 // RIGHT ARROW key
|
||||
VK_DOWN = 0x28 // DOWN ARROW key
|
||||
VK_SELECT = 0x29 // SELECT key
|
||||
VK_PRINT = 0x2A // PRINT key
|
||||
VK_EXECUTE = 0x2B // EXECUTE key
|
||||
VK_SNAPSHOT = 0x2C // PRINT SCREEN key
|
||||
VK_INSERT = 0x2D // INS key
|
||||
VK_DELETE = 0x2E // DEL key
|
||||
VK_HELP = 0x2F // HELP key
|
||||
VK_F1 = 0x70 // F1 key
|
||||
VK_F2 = 0x71 // F2 key
|
||||
VK_F3 = 0x72 // F3 key
|
||||
VK_F4 = 0x73 // F4 key
|
||||
VK_F5 = 0x74 // F5 key
|
||||
VK_F6 = 0x75 // F6 key
|
||||
VK_F7 = 0x76 // F7 key
|
||||
VK_F8 = 0x77 // F8 key
|
||||
VK_F9 = 0x78 // F9 key
|
||||
VK_F10 = 0x79 // F10 key
|
||||
VK_F11 = 0x7A // F11 key
|
||||
VK_F12 = 0x7B // F12 key
|
||||
|
||||
RIGHT_ALT_PRESSED = 0x0001
|
||||
LEFT_ALT_PRESSED = 0x0002
|
||||
RIGHT_CTRL_PRESSED = 0x0004
|
||||
LEFT_CTRL_PRESSED = 0x0008
|
||||
SHIFT_PRESSED = 0x0010
|
||||
NUMLOCK_ON = 0x0020
|
||||
SCROLLLOCK_ON = 0x0040
|
||||
CAPSLOCK_ON = 0x0080
|
||||
ENHANCED_KEY = 0x0100
|
||||
)
|
||||
|
||||
type ansiCommand struct {
|
||||
CommandBytes []byte
|
||||
Command string
|
||||
Parameters []string
|
||||
IsSpecial bool
|
||||
}
|
||||
|
||||
func newAnsiCommand(command []byte) *ansiCommand {
|
||||
|
||||
if isCharacterSelectionCmdChar(command[1]) {
|
||||
// Is Character Set Selection commands
|
||||
return &ansiCommand{
|
||||
CommandBytes: command,
|
||||
Command: string(command),
|
||||
IsSpecial: true,
|
||||
}
|
||||
}
|
||||
|
||||
// last char is command character
|
||||
lastCharIndex := len(command) - 1
|
||||
|
||||
ac := &ansiCommand{
|
||||
CommandBytes: command,
|
||||
Command: string(command[lastCharIndex]),
|
||||
IsSpecial: false,
|
||||
}
|
||||
|
||||
// more than a single escape
|
||||
if lastCharIndex != 0 {
|
||||
start := 1
|
||||
// skip if double char escape sequence
|
||||
if command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_ESCAPE_SECONDARY {
|
||||
start++
|
||||
}
|
||||
// convert this to GetNextParam method
|
||||
ac.Parameters = strings.Split(string(command[start:lastCharIndex]), ansiterm.ANSI_PARAMETER_SEP)
|
||||
}
|
||||
|
||||
return ac
|
||||
}
|
||||
|
||||
func (ac *ansiCommand) paramAsSHORT(index int, defaultValue int16) int16 {
|
||||
if index < 0 || index >= len(ac.Parameters) {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
param, err := strconv.ParseInt(ac.Parameters[index], 10, 16)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
return int16(param)
|
||||
}
|
||||
|
||||
func (ac *ansiCommand) String() string {
|
||||
return fmt.Sprintf("0x%v \"%v\" (\"%v\")",
|
||||
bytesToHex(ac.CommandBytes),
|
||||
ac.Command,
|
||||
strings.Join(ac.Parameters, "\",\""))
|
||||
}
|
||||
|
||||
// isAnsiCommandChar returns true if the passed byte falls within the range of ANSI commands.
|
||||
// See http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html.
|
||||
func isAnsiCommandChar(b byte) bool {
|
||||
switch {
|
||||
case ansiterm.ANSI_COMMAND_FIRST <= b && b <= ansiterm.ANSI_COMMAND_LAST && b != ansiterm.ANSI_ESCAPE_SECONDARY:
|
||||
return true
|
||||
case b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_OSC || b == ansiterm.ANSI_CMD_DECPAM || b == ansiterm.ANSI_CMD_DECPNM:
|
||||
// non-CSI escape sequence terminator
|
||||
return true
|
||||
case b == ansiterm.ANSI_CMD_STR_TERM || b == ansiterm.ANSI_BEL:
|
||||
// String escape sequence terminator
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isXtermOscSequence(command []byte, current byte) bool {
|
||||
return (len(command) >= 2 && command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_CMD_OSC && current != ansiterm.ANSI_BEL)
|
||||
}
|
||||
|
||||
func isCharacterSelectionCmdChar(b byte) bool {
|
||||
return (b == ansiterm.ANSI_CMD_G0 || b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_G2 || b == ansiterm.ANSI_CMD_G3)
|
||||
}
|
||||
|
||||
// bytesToHex converts a slice of bytes to a human-readable string.
|
||||
func bytesToHex(b []byte) string {
|
||||
hex := make([]string, len(b))
|
||||
for i, ch := range b {
|
||||
hex[i] = fmt.Sprintf("%X", ch)
|
||||
}
|
||||
return strings.Join(hex, "")
|
||||
}
|
||||
|
||||
// ensureInRange adjusts the passed value, if necessary, to ensure it is within
|
||||
// the passed min / max range.
|
||||
func ensureInRange(n int16, min int16, max int16) int16 {
|
||||
if n < min {
|
||||
return min
|
||||
} else if n > max {
|
||||
return max
|
||||
} else {
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
func GetStdFile(nFile int) (*os.File, uintptr) {
|
||||
var file *os.File
|
||||
switch nFile {
|
||||
case syscall.STD_INPUT_HANDLE:
|
||||
file = os.Stdin
|
||||
case syscall.STD_OUTPUT_HANDLE:
|
||||
file = os.Stdout
|
||||
case syscall.STD_ERROR_HANDLE:
|
||||
file = os.Stderr
|
||||
default:
|
||||
panic(fmt.Errorf("Invalid standard handle identifier: %v", nFile))
|
||||
}
|
||||
|
||||
fd, err := syscall.GetStdHandle(nFile)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Invalid standard handle indentifier: %v -- %v", nFile, err))
|
||||
}
|
||||
|
||||
return file, uintptr(fd)
|
||||
}
|
||||
322
cli/vendor/github.com/Azure/go-ansiterm/winterm/api.go
generated
vendored
322
cli/vendor/github.com/Azure/go-ansiterm/winterm/api.go
generated
vendored
@@ -1,322 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//===========================================================================================================
|
||||
// IMPORTANT NOTE:
|
||||
//
|
||||
// The methods below make extensive use of the "unsafe" package to obtain the required pointers.
|
||||
// Beginning in Go 1.3, the garbage collector may release local variables (e.g., incoming arguments, stack
|
||||
// variables) the pointers reference *before* the API completes.
|
||||
//
|
||||
// As a result, in those cases, the code must hint that the variables remain in active by invoking the
|
||||
// dummy method "use" (see below). Newer versions of Go are planned to change the mechanism to no longer
|
||||
// require unsafe pointers.
|
||||
//
|
||||
// If you add or modify methods, ENSURE protection of local variables through the "use" builtin to inform
|
||||
// the garbage collector the variables remain in use if:
|
||||
//
|
||||
// -- The value is not a pointer (e.g., int32, struct)
|
||||
// -- The value is not referenced by the method after passing the pointer to Windows
|
||||
//
|
||||
// See http://golang.org/doc/go1.3.
|
||||
//===========================================================================================================
|
||||
|
||||
var (
|
||||
kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
getConsoleCursorInfoProc = kernel32DLL.NewProc("GetConsoleCursorInfo")
|
||||
setConsoleCursorInfoProc = kernel32DLL.NewProc("SetConsoleCursorInfo")
|
||||
setConsoleCursorPositionProc = kernel32DLL.NewProc("SetConsoleCursorPosition")
|
||||
setConsoleModeProc = kernel32DLL.NewProc("SetConsoleMode")
|
||||
getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
|
||||
setConsoleScreenBufferSizeProc = kernel32DLL.NewProc("SetConsoleScreenBufferSize")
|
||||
scrollConsoleScreenBufferProc = kernel32DLL.NewProc("ScrollConsoleScreenBufferA")
|
||||
setConsoleTextAttributeProc = kernel32DLL.NewProc("SetConsoleTextAttribute")
|
||||
setConsoleWindowInfoProc = kernel32DLL.NewProc("SetConsoleWindowInfo")
|
||||
writeConsoleOutputProc = kernel32DLL.NewProc("WriteConsoleOutputW")
|
||||
readConsoleInputProc = kernel32DLL.NewProc("ReadConsoleInputW")
|
||||
waitForSingleObjectProc = kernel32DLL.NewProc("WaitForSingleObject")
|
||||
)
|
||||
|
||||
// Windows Console constants
|
||||
const (
|
||||
// Console modes
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
|
||||
ENABLE_PROCESSED_INPUT = 0x0001
|
||||
ENABLE_LINE_INPUT = 0x0002
|
||||
ENABLE_ECHO_INPUT = 0x0004
|
||||
ENABLE_WINDOW_INPUT = 0x0008
|
||||
ENABLE_MOUSE_INPUT = 0x0010
|
||||
ENABLE_INSERT_MODE = 0x0020
|
||||
ENABLE_QUICK_EDIT_MODE = 0x0040
|
||||
ENABLE_EXTENDED_FLAGS = 0x0080
|
||||
|
||||
ENABLE_PROCESSED_OUTPUT = 0x0001
|
||||
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
|
||||
|
||||
// Character attributes
|
||||
// Note:
|
||||
// -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan).
|
||||
// Clearing all foreground or background colors results in black; setting all creates white.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes.
|
||||
FOREGROUND_BLUE uint16 = 0x0001
|
||||
FOREGROUND_GREEN uint16 = 0x0002
|
||||
FOREGROUND_RED uint16 = 0x0004
|
||||
FOREGROUND_INTENSITY uint16 = 0x0008
|
||||
FOREGROUND_MASK uint16 = 0x000F
|
||||
|
||||
BACKGROUND_BLUE uint16 = 0x0010
|
||||
BACKGROUND_GREEN uint16 = 0x0020
|
||||
BACKGROUND_RED uint16 = 0x0040
|
||||
BACKGROUND_INTENSITY uint16 = 0x0080
|
||||
BACKGROUND_MASK uint16 = 0x00F0
|
||||
|
||||
COMMON_LVB_MASK uint16 = 0xFF00
|
||||
COMMON_LVB_REVERSE_VIDEO uint16 = 0x4000
|
||||
COMMON_LVB_UNDERSCORE uint16 = 0x8000
|
||||
|
||||
// Input event types
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
|
||||
KEY_EVENT = 0x0001
|
||||
MOUSE_EVENT = 0x0002
|
||||
WINDOW_BUFFER_SIZE_EVENT = 0x0004
|
||||
MENU_EVENT = 0x0008
|
||||
FOCUS_EVENT = 0x0010
|
||||
|
||||
// WaitForSingleObject return codes
|
||||
WAIT_ABANDONED = 0x00000080
|
||||
WAIT_FAILED = 0xFFFFFFFF
|
||||
WAIT_SIGNALED = 0x0000000
|
||||
WAIT_TIMEOUT = 0x00000102
|
||||
|
||||
// WaitForSingleObject wait duration
|
||||
WAIT_INFINITE = 0xFFFFFFFF
|
||||
WAIT_ONE_SECOND = 1000
|
||||
WAIT_HALF_SECOND = 500
|
||||
WAIT_QUARTER_SECOND = 250
|
||||
)
|
||||
|
||||
// Windows API Console types
|
||||
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682101(v=vs.85).aspx for Console specific types (e.g., COORD)
|
||||
// -- See https://msdn.microsoft.com/en-us/library/aa296569(v=vs.60).aspx for comments on alignment
|
||||
type (
|
||||
CHAR_INFO struct {
|
||||
UnicodeChar uint16
|
||||
Attributes uint16
|
||||
}
|
||||
|
||||
CONSOLE_CURSOR_INFO struct {
|
||||
Size uint32
|
||||
Visible int32
|
||||
}
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO struct {
|
||||
Size COORD
|
||||
CursorPosition COORD
|
||||
Attributes uint16
|
||||
Window SMALL_RECT
|
||||
MaximumWindowSize COORD
|
||||
}
|
||||
|
||||
COORD struct {
|
||||
X int16
|
||||
Y int16
|
||||
}
|
||||
|
||||
SMALL_RECT struct {
|
||||
Left int16
|
||||
Top int16
|
||||
Right int16
|
||||
Bottom int16
|
||||
}
|
||||
|
||||
// INPUT_RECORD is a C/C++ union of which KEY_EVENT_RECORD is one case, it is also the largest
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
|
||||
INPUT_RECORD struct {
|
||||
EventType uint16
|
||||
KeyEvent KEY_EVENT_RECORD
|
||||
}
|
||||
|
||||
KEY_EVENT_RECORD struct {
|
||||
KeyDown int32
|
||||
RepeatCount uint16
|
||||
VirtualKeyCode uint16
|
||||
VirtualScanCode uint16
|
||||
UnicodeChar uint16
|
||||
ControlKeyState uint32
|
||||
}
|
||||
|
||||
WINDOW_BUFFER_SIZE struct {
|
||||
Size COORD
|
||||
}
|
||||
)
|
||||
|
||||
// boolToBOOL converts a Go bool into a Windows int32.
|
||||
func boolToBOOL(f bool) int32 {
|
||||
if f {
|
||||
return int32(1)
|
||||
} else {
|
||||
return int32(0)
|
||||
}
|
||||
}
|
||||
|
||||
// GetConsoleCursorInfo retrieves information about the size and visiblity of the console cursor.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683163(v=vs.85).aspx.
|
||||
func GetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error {
|
||||
r1, r2, err := getConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// SetConsoleCursorInfo sets the size and visiblity of the console cursor.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686019(v=vs.85).aspx.
|
||||
func SetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error {
|
||||
r1, r2, err := setConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// SetConsoleCursorPosition location of the console cursor.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx.
|
||||
func SetConsoleCursorPosition(handle uintptr, coord COORD) error {
|
||||
r1, r2, err := setConsoleCursorPositionProc.Call(handle, coordToPointer(coord))
|
||||
use(coord)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// GetConsoleMode gets the console mode for given file descriptor
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx.
|
||||
func GetConsoleMode(handle uintptr) (mode uint32, err error) {
|
||||
err = syscall.GetConsoleMode(syscall.Handle(handle), &mode)
|
||||
return mode, err
|
||||
}
|
||||
|
||||
// SetConsoleMode sets the console mode for given file descriptor
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
|
||||
func SetConsoleMode(handle uintptr, mode uint32) error {
|
||||
r1, r2, err := setConsoleModeProc.Call(handle, uintptr(mode), 0)
|
||||
use(mode)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// GetConsoleScreenBufferInfo retrieves information about the specified console screen buffer.
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx.
|
||||
func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) {
|
||||
info := CONSOLE_SCREEN_BUFFER_INFO{}
|
||||
err := checkError(getConsoleScreenBufferInfoProc.Call(handle, uintptr(unsafe.Pointer(&info)), 0))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func ScrollConsoleScreenBuffer(handle uintptr, scrollRect SMALL_RECT, clipRect SMALL_RECT, destOrigin COORD, char CHAR_INFO) error {
|
||||
r1, r2, err := scrollConsoleScreenBufferProc.Call(handle, uintptr(unsafe.Pointer(&scrollRect)), uintptr(unsafe.Pointer(&clipRect)), coordToPointer(destOrigin), uintptr(unsafe.Pointer(&char)))
|
||||
use(scrollRect)
|
||||
use(clipRect)
|
||||
use(destOrigin)
|
||||
use(char)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// SetConsoleScreenBufferSize sets the size of the console screen buffer.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686044(v=vs.85).aspx.
|
||||
func SetConsoleScreenBufferSize(handle uintptr, coord COORD) error {
|
||||
r1, r2, err := setConsoleScreenBufferSizeProc.Call(handle, coordToPointer(coord))
|
||||
use(coord)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// SetConsoleTextAttribute sets the attributes of characters written to the
|
||||
// console screen buffer by the WriteFile or WriteConsole function.
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx.
|
||||
func SetConsoleTextAttribute(handle uintptr, attribute uint16) error {
|
||||
r1, r2, err := setConsoleTextAttributeProc.Call(handle, uintptr(attribute), 0)
|
||||
use(attribute)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// SetConsoleWindowInfo sets the size and position of the console screen buffer's window.
|
||||
// Note that the size and location must be within and no larger than the backing console screen buffer.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686125(v=vs.85).aspx.
|
||||
func SetConsoleWindowInfo(handle uintptr, isAbsolute bool, rect SMALL_RECT) error {
|
||||
r1, r2, err := setConsoleWindowInfoProc.Call(handle, uintptr(boolToBOOL(isAbsolute)), uintptr(unsafe.Pointer(&rect)))
|
||||
use(isAbsolute)
|
||||
use(rect)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// WriteConsoleOutput writes the CHAR_INFOs from the provided buffer to the active console buffer.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687404(v=vs.85).aspx.
|
||||
func WriteConsoleOutput(handle uintptr, buffer []CHAR_INFO, bufferSize COORD, bufferCoord COORD, writeRegion *SMALL_RECT) error {
|
||||
r1, r2, err := writeConsoleOutputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), coordToPointer(bufferSize), coordToPointer(bufferCoord), uintptr(unsafe.Pointer(writeRegion)))
|
||||
use(buffer)
|
||||
use(bufferSize)
|
||||
use(bufferCoord)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// ReadConsoleInput reads (and removes) data from the console input buffer.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx.
|
||||
func ReadConsoleInput(handle uintptr, buffer []INPUT_RECORD, count *uint32) error {
|
||||
r1, r2, err := readConsoleInputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(count)))
|
||||
use(buffer)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// WaitForSingleObject waits for the passed handle to be signaled.
|
||||
// It returns true if the handle was signaled; false otherwise.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx.
|
||||
func WaitForSingleObject(handle uintptr, msWait uint32) (bool, error) {
|
||||
r1, _, err := waitForSingleObjectProc.Call(handle, uintptr(uint32(msWait)))
|
||||
switch r1 {
|
||||
case WAIT_ABANDONED, WAIT_TIMEOUT:
|
||||
return false, nil
|
||||
case WAIT_SIGNALED:
|
||||
return true, nil
|
||||
}
|
||||
use(msWait)
|
||||
return false, err
|
||||
}
|
||||
|
||||
// String helpers
|
||||
func (info CONSOLE_SCREEN_BUFFER_INFO) String() string {
|
||||
return fmt.Sprintf("Size(%v) Cursor(%v) Window(%v) Max(%v)", info.Size, info.CursorPosition, info.Window, info.MaximumWindowSize)
|
||||
}
|
||||
|
||||
func (coord COORD) String() string {
|
||||
return fmt.Sprintf("%v,%v", coord.X, coord.Y)
|
||||
}
|
||||
|
||||
func (rect SMALL_RECT) String() string {
|
||||
return fmt.Sprintf("(%v,%v),(%v,%v)", rect.Left, rect.Top, rect.Right, rect.Bottom)
|
||||
}
|
||||
|
||||
// checkError evaluates the results of a Windows API call and returns the error if it failed.
|
||||
func checkError(r1, r2 uintptr, err error) error {
|
||||
// Windows APIs return non-zero to indicate success
|
||||
if r1 != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return the error if provided, otherwise default to EINVAL
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return syscall.EINVAL
|
||||
}
|
||||
|
||||
// coordToPointer converts a COORD into a uintptr (by fooling the type system).
|
||||
func coordToPointer(c COORD) uintptr {
|
||||
// Note: This code assumes the two SHORTs are correctly laid out; the "cast" to uint32 is just to get a pointer to pass.
|
||||
return uintptr(*((*uint32)(unsafe.Pointer(&c))))
|
||||
}
|
||||
|
||||
// use is a no-op, but the compiler cannot see that it is.
|
||||
// Calling use(p) ensures that p is kept live until that point.
|
||||
func use(p interface{}) {}
|
||||
100
cli/vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go
generated
vendored
100
cli/vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go
generated
vendored
@@ -1,100 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
import "github.com/Azure/go-ansiterm"
|
||||
|
||||
const (
|
||||
FOREGROUND_COLOR_MASK = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
||||
BACKGROUND_COLOR_MASK = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
|
||||
)
|
||||
|
||||
// collectAnsiIntoWindowsAttributes modifies the passed Windows text mode flags to reflect the
|
||||
// request represented by the passed ANSI mode.
|
||||
func collectAnsiIntoWindowsAttributes(windowsMode uint16, inverted bool, baseMode uint16, ansiMode int16) (uint16, bool) {
|
||||
switch ansiMode {
|
||||
|
||||
// Mode styles
|
||||
case ansiterm.ANSI_SGR_BOLD:
|
||||
windowsMode = windowsMode | FOREGROUND_INTENSITY
|
||||
|
||||
case ansiterm.ANSI_SGR_DIM, ansiterm.ANSI_SGR_BOLD_DIM_OFF:
|
||||
windowsMode &^= FOREGROUND_INTENSITY
|
||||
|
||||
case ansiterm.ANSI_SGR_UNDERLINE:
|
||||
windowsMode = windowsMode | COMMON_LVB_UNDERSCORE
|
||||
|
||||
case ansiterm.ANSI_SGR_REVERSE:
|
||||
inverted = true
|
||||
|
||||
case ansiterm.ANSI_SGR_REVERSE_OFF:
|
||||
inverted = false
|
||||
|
||||
case ansiterm.ANSI_SGR_UNDERLINE_OFF:
|
||||
windowsMode &^= COMMON_LVB_UNDERSCORE
|
||||
|
||||
// Foreground colors
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_DEFAULT:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_MASK) | (baseMode & FOREGROUND_MASK)
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_BLACK:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK)
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_RED:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_GREEN:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_YELLOW:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_BLUE:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_BLUE
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_MAGENTA:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_BLUE
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_CYAN:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN | FOREGROUND_BLUE
|
||||
|
||||
case ansiterm.ANSI_SGR_FOREGROUND_WHITE:
|
||||
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
||||
|
||||
// Background colors
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_DEFAULT:
|
||||
// Black with no intensity
|
||||
windowsMode = (windowsMode &^ BACKGROUND_MASK) | (baseMode & BACKGROUND_MASK)
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_BLACK:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK)
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_RED:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_GREEN:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_YELLOW:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_BLUE:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_BLUE
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_MAGENTA:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_BLUE
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_CYAN:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN | BACKGROUND_BLUE
|
||||
|
||||
case ansiterm.ANSI_SGR_BACKGROUND_WHITE:
|
||||
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
|
||||
}
|
||||
|
||||
return windowsMode, inverted
|
||||
}
|
||||
|
||||
// invertAttributes inverts the foreground and background colors of a Windows attributes value
|
||||
func invertAttributes(windowsMode uint16) uint16 {
|
||||
return (COMMON_LVB_MASK & windowsMode) | ((FOREGROUND_MASK & windowsMode) << 4) | ((BACKGROUND_MASK & windowsMode) >> 4)
|
||||
}
|
||||
101
cli/vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go
generated
vendored
101
cli/vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go
generated
vendored
@@ -1,101 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
const (
|
||||
horizontal = iota
|
||||
vertical
|
||||
)
|
||||
|
||||
func (h *windowsAnsiEventHandler) getCursorWindow(info *CONSOLE_SCREEN_BUFFER_INFO) SMALL_RECT {
|
||||
if h.originMode {
|
||||
sr := h.effectiveSr(info.Window)
|
||||
return SMALL_RECT{
|
||||
Top: sr.top,
|
||||
Bottom: sr.bottom,
|
||||
Left: 0,
|
||||
Right: info.Size.X - 1,
|
||||
}
|
||||
} else {
|
||||
return SMALL_RECT{
|
||||
Top: info.Window.Top,
|
||||
Bottom: info.Window.Bottom,
|
||||
Left: 0,
|
||||
Right: info.Size.X - 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setCursorPosition sets the cursor to the specified position, bounded to the screen size
|
||||
func (h *windowsAnsiEventHandler) setCursorPosition(position COORD, window SMALL_RECT) error {
|
||||
position.X = ensureInRange(position.X, window.Left, window.Right)
|
||||
position.Y = ensureInRange(position.Y, window.Top, window.Bottom)
|
||||
err := SetConsoleCursorPosition(h.fd, position)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("Cursor position set: (%d, %d)", position.X, position.Y)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) moveCursorVertical(param int) error {
|
||||
return h.moveCursor(vertical, param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) moveCursorHorizontal(param int) error {
|
||||
return h.moveCursor(horizontal, param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) moveCursor(moveMode int, param int) error {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
position := info.CursorPosition
|
||||
switch moveMode {
|
||||
case horizontal:
|
||||
position.X += int16(param)
|
||||
case vertical:
|
||||
position.Y += int16(param)
|
||||
}
|
||||
|
||||
if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) moveCursorLine(param int) error {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
position := info.CursorPosition
|
||||
position.X = 0
|
||||
position.Y += int16(param)
|
||||
|
||||
if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) moveCursorColumn(param int) error {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
position := info.CursorPosition
|
||||
position.X = int16(param) - 1
|
||||
|
||||
if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
84
cli/vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go
generated
vendored
84
cli/vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go
generated
vendored
@@ -1,84 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
import "github.com/Azure/go-ansiterm"
|
||||
|
||||
func (h *windowsAnsiEventHandler) clearRange(attributes uint16, fromCoord COORD, toCoord COORD) error {
|
||||
// Ignore an invalid (negative area) request
|
||||
if toCoord.Y < fromCoord.Y {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
var coordStart = COORD{}
|
||||
var coordEnd = COORD{}
|
||||
|
||||
xCurrent, yCurrent := fromCoord.X, fromCoord.Y
|
||||
xEnd, yEnd := toCoord.X, toCoord.Y
|
||||
|
||||
// Clear any partial initial line
|
||||
if xCurrent > 0 {
|
||||
coordStart.X, coordStart.Y = xCurrent, yCurrent
|
||||
coordEnd.X, coordEnd.Y = xEnd, yCurrent
|
||||
|
||||
err = h.clearRect(attributes, coordStart, coordEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
xCurrent = 0
|
||||
yCurrent += 1
|
||||
}
|
||||
|
||||
// Clear intervening rectangular section
|
||||
if yCurrent < yEnd {
|
||||
coordStart.X, coordStart.Y = xCurrent, yCurrent
|
||||
coordEnd.X, coordEnd.Y = xEnd, yEnd-1
|
||||
|
||||
err = h.clearRect(attributes, coordStart, coordEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
xCurrent = 0
|
||||
yCurrent = yEnd
|
||||
}
|
||||
|
||||
// Clear remaining partial ending line
|
||||
coordStart.X, coordStart.Y = xCurrent, yCurrent
|
||||
coordEnd.X, coordEnd.Y = xEnd, yEnd
|
||||
|
||||
err = h.clearRect(attributes, coordStart, coordEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) clearRect(attributes uint16, fromCoord COORD, toCoord COORD) error {
|
||||
region := SMALL_RECT{Top: fromCoord.Y, Left: fromCoord.X, Bottom: toCoord.Y, Right: toCoord.X}
|
||||
width := toCoord.X - fromCoord.X + 1
|
||||
height := toCoord.Y - fromCoord.Y + 1
|
||||
size := uint32(width) * uint32(height)
|
||||
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
buffer := make([]CHAR_INFO, size)
|
||||
|
||||
char := CHAR_INFO{ansiterm.FILL_CHARACTER, attributes}
|
||||
for i := 0; i < int(size); i++ {
|
||||
buffer[i] = char
|
||||
}
|
||||
|
||||
err := WriteConsoleOutput(h.fd, buffer, COORD{X: width, Y: height}, COORD{X: 0, Y: 0}, ®ion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
118
cli/vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go
generated
vendored
118
cli/vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go
generated
vendored
@@ -1,118 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
// effectiveSr gets the current effective scroll region in buffer coordinates
|
||||
func (h *windowsAnsiEventHandler) effectiveSr(window SMALL_RECT) scrollRegion {
|
||||
top := addInRange(window.Top, h.sr.top, window.Top, window.Bottom)
|
||||
bottom := addInRange(window.Top, h.sr.bottom, window.Top, window.Bottom)
|
||||
if top >= bottom {
|
||||
top = window.Top
|
||||
bottom = window.Bottom
|
||||
}
|
||||
return scrollRegion{top: top, bottom: bottom}
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) scrollUp(param int) error {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sr := h.effectiveSr(info.Window)
|
||||
return h.scroll(param, sr, info)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) scrollDown(param int) error {
|
||||
return h.scrollUp(-param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) deleteLines(param int) error {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
start := info.CursorPosition.Y
|
||||
sr := h.effectiveSr(info.Window)
|
||||
// Lines cannot be inserted or deleted outside the scrolling region.
|
||||
if start >= sr.top && start <= sr.bottom {
|
||||
sr.top = start
|
||||
return h.scroll(param, sr, info)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) insertLines(param int) error {
|
||||
return h.deleteLines(-param)
|
||||
}
|
||||
|
||||
// scroll scrolls the provided scroll region by param lines. The scroll region is in buffer coordinates.
|
||||
func (h *windowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSOLE_SCREEN_BUFFER_INFO) error {
|
||||
logger.Infof("scroll: scrollTop: %d, scrollBottom: %d", sr.top, sr.bottom)
|
||||
logger.Infof("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom)
|
||||
|
||||
// Copy from and clip to the scroll region (full buffer width)
|
||||
scrollRect := SMALL_RECT{
|
||||
Top: sr.top,
|
||||
Bottom: sr.bottom,
|
||||
Left: 0,
|
||||
Right: info.Size.X - 1,
|
||||
}
|
||||
|
||||
// Origin to which area should be copied
|
||||
destOrigin := COORD{
|
||||
X: 0,
|
||||
Y: sr.top - int16(param),
|
||||
}
|
||||
|
||||
char := CHAR_INFO{
|
||||
UnicodeChar: ' ',
|
||||
Attributes: h.attributes,
|
||||
}
|
||||
|
||||
if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) deleteCharacters(param int) error {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return h.scrollLine(param, info.CursorPosition, info)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) insertCharacters(param int) error {
|
||||
return h.deleteCharacters(-param)
|
||||
}
|
||||
|
||||
// scrollLine scrolls a line horizontally starting at the provided position by a number of columns.
|
||||
func (h *windowsAnsiEventHandler) scrollLine(columns int, position COORD, info *CONSOLE_SCREEN_BUFFER_INFO) error {
|
||||
// Copy from and clip to the scroll region (full buffer width)
|
||||
scrollRect := SMALL_RECT{
|
||||
Top: position.Y,
|
||||
Bottom: position.Y,
|
||||
Left: position.X,
|
||||
Right: info.Size.X - 1,
|
||||
}
|
||||
|
||||
// Origin to which area should be copied
|
||||
destOrigin := COORD{
|
||||
X: position.X - int16(columns),
|
||||
Y: position.Y,
|
||||
}
|
||||
|
||||
char := CHAR_INFO{
|
||||
UnicodeChar: ' ',
|
||||
Attributes: h.attributes,
|
||||
}
|
||||
|
||||
if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
9
cli/vendor/github.com/Azure/go-ansiterm/winterm/utilities.go
generated
vendored
9
cli/vendor/github.com/Azure/go-ansiterm/winterm/utilities.go
generated
vendored
@@ -1,9 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
// AddInRange increments a value by the passed quantity while ensuring the values
|
||||
// always remain within the supplied min / max range.
|
||||
func addInRange(n int16, increment int16, min int16, max int16) int16 {
|
||||
return ensureInRange(n+increment, min, max)
|
||||
}
|
||||
726
cli/vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go
generated
vendored
726
cli/vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go
generated
vendored
@@ -1,726 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/Azure/go-ansiterm"
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
var logger *logrus.Logger
|
||||
|
||||
type windowsAnsiEventHandler struct {
|
||||
fd uintptr
|
||||
file *os.File
|
||||
infoReset *CONSOLE_SCREEN_BUFFER_INFO
|
||||
sr scrollRegion
|
||||
buffer bytes.Buffer
|
||||
attributes uint16
|
||||
inverted bool
|
||||
wrapNext bool
|
||||
drewMarginByte bool
|
||||
originMode bool
|
||||
marginByte byte
|
||||
curInfo *CONSOLE_SCREEN_BUFFER_INFO
|
||||
curPos COORD
|
||||
}
|
||||
|
||||
func CreateWinEventHandler(fd uintptr, file *os.File) ansiterm.AnsiEventHandler {
|
||||
logFile := ioutil.Discard
|
||||
|
||||
if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
|
||||
logFile, _ = os.Create("winEventHandler.log")
|
||||
}
|
||||
|
||||
logger = &logrus.Logger{
|
||||
Out: logFile,
|
||||
Formatter: new(logrus.TextFormatter),
|
||||
Level: logrus.DebugLevel,
|
||||
}
|
||||
|
||||
infoReset, err := GetConsoleScreenBufferInfo(fd)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &windowsAnsiEventHandler{
|
||||
fd: fd,
|
||||
file: file,
|
||||
infoReset: infoReset,
|
||||
attributes: infoReset.Attributes,
|
||||
}
|
||||
}
|
||||
|
||||
type scrollRegion struct {
|
||||
top int16
|
||||
bottom int16
|
||||
}
|
||||
|
||||
// simulateLF simulates a LF or CR+LF by scrolling if necessary to handle the
|
||||
// current cursor position and scroll region settings, in which case it returns
|
||||
// true. If no special handling is necessary, then it does nothing and returns
|
||||
// false.
|
||||
//
|
||||
// In the false case, the caller should ensure that a carriage return
|
||||
// and line feed are inserted or that the text is otherwise wrapped.
|
||||
func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
|
||||
if h.wrapNext {
|
||||
if err := h.Flush(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
h.clearWrap()
|
||||
}
|
||||
pos, info, err := h.getCurrentInfo()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
sr := h.effectiveSr(info.Window)
|
||||
if pos.Y == sr.bottom {
|
||||
// Scrolling is necessary. Let Windows automatically scroll if the scrolling region
|
||||
// is the full window.
|
||||
if sr.top == info.Window.Top && sr.bottom == info.Window.Bottom {
|
||||
if includeCR {
|
||||
pos.X = 0
|
||||
h.updatePos(pos)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// A custom scroll region is active. Scroll the window manually to simulate
|
||||
// the LF.
|
||||
if err := h.Flush(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
logger.Info("Simulating LF inside scroll region")
|
||||
if err := h.scrollUp(1); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if includeCR {
|
||||
pos.X = 0
|
||||
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
|
||||
} else if pos.Y < info.Window.Bottom {
|
||||
// Let Windows handle the LF.
|
||||
pos.Y++
|
||||
if includeCR {
|
||||
pos.X = 0
|
||||
}
|
||||
h.updatePos(pos)
|
||||
return false, nil
|
||||
} else {
|
||||
// The cursor is at the bottom of the screen but outside the scroll
|
||||
// region. Skip the LF.
|
||||
logger.Info("Simulating LF outside scroll region")
|
||||
if includeCR {
|
||||
if err := h.Flush(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
pos.X = 0
|
||||
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// executeLF executes a LF without a CR.
|
||||
func (h *windowsAnsiEventHandler) executeLF() error {
|
||||
handled, err := h.simulateLF(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !handled {
|
||||
// Windows LF will reset the cursor column position. Write the LF
|
||||
// and restore the cursor position.
|
||||
pos, _, err := h.getCurrentInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED)
|
||||
if pos.X != 0 {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Info("Resetting cursor position for LF without CR")
|
||||
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) Print(b byte) error {
|
||||
if h.wrapNext {
|
||||
h.buffer.WriteByte(h.marginByte)
|
||||
h.clearWrap()
|
||||
if _, err := h.simulateLF(true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
pos, info, err := h.getCurrentInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pos.X == info.Size.X-1 {
|
||||
h.wrapNext = true
|
||||
h.marginByte = b
|
||||
} else {
|
||||
pos.X++
|
||||
h.updatePos(pos)
|
||||
h.buffer.WriteByte(b)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) Execute(b byte) error {
|
||||
switch b {
|
||||
case ansiterm.ANSI_TAB:
|
||||
logger.Info("Execute(TAB)")
|
||||
// Move to the next tab stop, but preserve auto-wrap if already set.
|
||||
if !h.wrapNext {
|
||||
pos, info, err := h.getCurrentInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pos.X = (pos.X + 8) - pos.X%8
|
||||
if pos.X >= info.Size.X {
|
||||
pos.X = info.Size.X - 1
|
||||
}
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case ansiterm.ANSI_BEL:
|
||||
h.buffer.WriteByte(ansiterm.ANSI_BEL)
|
||||
return nil
|
||||
|
||||
case ansiterm.ANSI_BACKSPACE:
|
||||
if h.wrapNext {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.clearWrap()
|
||||
}
|
||||
pos, _, err := h.getCurrentInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pos.X > 0 {
|
||||
pos.X--
|
||||
h.updatePos(pos)
|
||||
h.buffer.WriteByte(ansiterm.ANSI_BACKSPACE)
|
||||
}
|
||||
return nil
|
||||
|
||||
case ansiterm.ANSI_VERTICAL_TAB, ansiterm.ANSI_FORM_FEED:
|
||||
// Treat as true LF.
|
||||
return h.executeLF()
|
||||
|
||||
case ansiterm.ANSI_LINE_FEED:
|
||||
// Simulate a CR and LF for now since there is no way in go-ansiterm
|
||||
// to tell if the LF should include CR (and more things break when it's
|
||||
// missing than when it's incorrectly added).
|
||||
handled, err := h.simulateLF(true)
|
||||
if handled || err != nil {
|
||||
return err
|
||||
}
|
||||
return h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED)
|
||||
|
||||
case ansiterm.ANSI_CARRIAGE_RETURN:
|
||||
if h.wrapNext {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
h.clearWrap()
|
||||
}
|
||||
pos, _, err := h.getCurrentInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pos.X != 0 {
|
||||
pos.X = 0
|
||||
h.updatePos(pos)
|
||||
h.buffer.WriteByte(ansiterm.ANSI_CARRIAGE_RETURN)
|
||||
}
|
||||
return nil
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CUU(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("CUU: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorVertical(-param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CUD(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("CUD: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorVertical(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CUF(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("CUF: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorHorizontal(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CUB(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("CUB: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorHorizontal(-param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CNL(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("CNL: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorLine(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CPL(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("CPL: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorLine(-param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CHA(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("CHA: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.moveCursorColumn(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) VPA(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("VPA: [[%d]]", param)
|
||||
h.clearWrap()
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
window := h.getCursorWindow(info)
|
||||
position := info.CursorPosition
|
||||
position.Y = window.Top + int16(param) - 1
|
||||
return h.setCursorPosition(position, window)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) CUP(row int, col int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("CUP: [[%d %d]]", row, col)
|
||||
h.clearWrap()
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
window := h.getCursorWindow(info)
|
||||
position := COORD{window.Left + int16(col) - 1, window.Top + int16(row) - 1}
|
||||
return h.setCursorPosition(position, window)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) HVP(row int, col int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("HVP: [[%d %d]]", row, col)
|
||||
h.clearWrap()
|
||||
return h.CUP(row, col)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DECTCEM(visible bool) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("DECTCEM: [%v]", []string{strconv.FormatBool(visible)})
|
||||
h.clearWrap()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DECOM(enable bool) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("DECOM: [%v]", []string{strconv.FormatBool(enable)})
|
||||
h.clearWrap()
|
||||
h.originMode = enable
|
||||
return h.CUP(1, 1)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("DECCOLM: [%v]", []string{strconv.FormatBool(use132)})
|
||||
h.clearWrap()
|
||||
if err := h.ED(2); err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetWidth := int16(80)
|
||||
if use132 {
|
||||
targetWidth = 132
|
||||
}
|
||||
if info.Size.X < targetWidth {
|
||||
if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
|
||||
logger.Info("set buffer failed:", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
window := info.Window
|
||||
window.Left = 0
|
||||
window.Right = targetWidth - 1
|
||||
if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
|
||||
logger.Info("set window failed:", err)
|
||||
return err
|
||||
}
|
||||
if info.Size.X > targetWidth {
|
||||
if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
|
||||
logger.Info("set buffer failed:", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return SetConsoleCursorPosition(h.fd, COORD{0, 0})
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) ED(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("ED: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
|
||||
// [J -- Erases from the cursor to the end of the screen, including the cursor position.
|
||||
// [1J -- Erases from the beginning of the screen to the cursor, including the cursor position.
|
||||
// [2J -- Erases the complete display. The cursor does not move.
|
||||
// Notes:
|
||||
// -- Clearing the entire buffer, versus just the Window, works best for Windows Consoles
|
||||
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var start COORD
|
||||
var end COORD
|
||||
|
||||
switch param {
|
||||
case 0:
|
||||
start = info.CursorPosition
|
||||
end = COORD{info.Size.X - 1, info.Size.Y - 1}
|
||||
|
||||
case 1:
|
||||
start = COORD{0, 0}
|
||||
end = info.CursorPosition
|
||||
|
||||
case 2:
|
||||
start = COORD{0, 0}
|
||||
end = COORD{info.Size.X - 1, info.Size.Y - 1}
|
||||
}
|
||||
|
||||
err = h.clearRange(h.attributes, start, end)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the whole buffer was cleared, move the window to the top while preserving
|
||||
// the window-relative cursor position.
|
||||
if param == 2 {
|
||||
pos := info.CursorPosition
|
||||
window := info.Window
|
||||
pos.Y -= window.Top
|
||||
window.Bottom -= window.Top
|
||||
window.Top = 0
|
||||
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) EL(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("EL: [%v]", strconv.Itoa(param))
|
||||
h.clearWrap()
|
||||
|
||||
// [K -- Erases from the cursor to the end of the line, including the cursor position.
|
||||
// [1K -- Erases from the beginning of the line to the cursor, including the cursor position.
|
||||
// [2K -- Erases the complete line.
|
||||
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var start COORD
|
||||
var end COORD
|
||||
|
||||
switch param {
|
||||
case 0:
|
||||
start = info.CursorPosition
|
||||
end = COORD{info.Size.X, info.CursorPosition.Y}
|
||||
|
||||
case 1:
|
||||
start = COORD{0, info.CursorPosition.Y}
|
||||
end = info.CursorPosition
|
||||
|
||||
case 2:
|
||||
start = COORD{0, info.CursorPosition.Y}
|
||||
end = COORD{info.Size.X, info.CursorPosition.Y}
|
||||
}
|
||||
|
||||
err = h.clearRange(h.attributes, start, end)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) IL(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("IL: [%v]", strconv.Itoa(param))
|
||||
h.clearWrap()
|
||||
return h.insertLines(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DL(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("DL: [%v]", strconv.Itoa(param))
|
||||
h.clearWrap()
|
||||
return h.deleteLines(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) ICH(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("ICH: [%v]", strconv.Itoa(param))
|
||||
h.clearWrap()
|
||||
return h.insertCharacters(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DCH(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("DCH: [%v]", strconv.Itoa(param))
|
||||
h.clearWrap()
|
||||
return h.deleteCharacters(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) SGR(params []int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
strings := []string{}
|
||||
for _, v := range params {
|
||||
strings = append(strings, strconv.Itoa(v))
|
||||
}
|
||||
|
||||
logger.Infof("SGR: [%v]", strings)
|
||||
|
||||
if len(params) <= 0 {
|
||||
h.attributes = h.infoReset.Attributes
|
||||
h.inverted = false
|
||||
} else {
|
||||
for _, attr := range params {
|
||||
|
||||
if attr == ansiterm.ANSI_SGR_RESET {
|
||||
h.attributes = h.infoReset.Attributes
|
||||
h.inverted = false
|
||||
continue
|
||||
}
|
||||
|
||||
h.attributes, h.inverted = collectAnsiIntoWindowsAttributes(h.attributes, h.inverted, h.infoReset.Attributes, int16(attr))
|
||||
}
|
||||
}
|
||||
|
||||
attributes := h.attributes
|
||||
if h.inverted {
|
||||
attributes = invertAttributes(attributes)
|
||||
}
|
||||
err := SetConsoleTextAttribute(h.fd, attributes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) SU(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("SU: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.scrollUp(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) SD(param int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("SD: [%v]", []string{strconv.Itoa(param)})
|
||||
h.clearWrap()
|
||||
return h.scrollDown(param)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DA(params []string) error {
|
||||
logger.Infof("DA: [%v]", params)
|
||||
// DA cannot be implemented because it must send data on the VT100 input stream,
|
||||
// which is not available to go-ansiterm.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) DECSTBM(top int, bottom int) error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("DECSTBM: [%d, %d]", top, bottom)
|
||||
|
||||
// Windows is 0 indexed, Linux is 1 indexed
|
||||
h.sr.top = int16(top - 1)
|
||||
h.sr.bottom = int16(bottom - 1)
|
||||
|
||||
// This command also moves the cursor to the origin.
|
||||
h.clearWrap()
|
||||
return h.CUP(1, 1)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) RI() error {
|
||||
if err := h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Info("RI: []")
|
||||
h.clearWrap()
|
||||
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sr := h.effectiveSr(info.Window)
|
||||
if info.CursorPosition.Y == sr.top {
|
||||
return h.scrollDown(1)
|
||||
}
|
||||
|
||||
return h.moveCursorVertical(-1)
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) IND() error {
|
||||
logger.Info("IND: []")
|
||||
return h.executeLF()
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) Flush() error {
|
||||
h.curInfo = nil
|
||||
if h.buffer.Len() > 0 {
|
||||
logger.Infof("Flush: [%s]", h.buffer.Bytes())
|
||||
if _, err := h.buffer.WriteTo(h.file); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if h.wrapNext && !h.drewMarginByte {
|
||||
logger.Infof("Flush: drawing margin byte '%c'", h.marginByte)
|
||||
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
charInfo := []CHAR_INFO{{UnicodeChar: uint16(h.marginByte), Attributes: info.Attributes}}
|
||||
size := COORD{1, 1}
|
||||
position := COORD{0, 0}
|
||||
region := SMALL_RECT{Left: info.CursorPosition.X, Top: info.CursorPosition.Y, Right: info.CursorPosition.X, Bottom: info.CursorPosition.Y}
|
||||
if err := WriteConsoleOutput(h.fd, charInfo, size, position, ®ion); err != nil {
|
||||
return err
|
||||
}
|
||||
h.drewMarginByte = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// cacheConsoleInfo ensures that the current console screen information has been queried
|
||||
// since the last call to Flush(). It must be called before accessing h.curInfo or h.curPos.
|
||||
func (h *windowsAnsiEventHandler) getCurrentInfo() (COORD, *CONSOLE_SCREEN_BUFFER_INFO, error) {
|
||||
if h.curInfo == nil {
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return COORD{}, nil, err
|
||||
}
|
||||
h.curInfo = info
|
||||
h.curPos = info.CursorPosition
|
||||
}
|
||||
return h.curPos, h.curInfo, nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) updatePos(pos COORD) {
|
||||
if h.curInfo == nil {
|
||||
panic("failed to call getCurrentInfo before calling updatePos")
|
||||
}
|
||||
h.curPos = pos
|
||||
}
|
||||
|
||||
// clearWrap clears the state where the cursor is in the margin
|
||||
// waiting for the next character before wrapping the line. This must
|
||||
// be done before most operations that act on the cursor.
|
||||
func (h *windowsAnsiEventHandler) clearWrap() {
|
||||
h.wrapNext = false
|
||||
h.drewMarginByte = false
|
||||
}
|
||||
26
cli/vendor/github.com/Nvveen/Gotty/LICENSE
generated
vendored
26
cli/vendor/github.com/Nvveen/Gotty/LICENSE
generated
vendored
@@ -1,26 +0,0 @@
|
||||
Copyright (c) 2012, Neal van Veen (nealvanveen@gmail.com)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those
|
||||
of the authors and should not be interpreted as representing official policies,
|
||||
either expressed or implied, of the FreeBSD Project.
|
||||
5
cli/vendor/github.com/Nvveen/Gotty/README
generated
vendored
5
cli/vendor/github.com/Nvveen/Gotty/README
generated
vendored
@@ -1,5 +0,0 @@
|
||||
Gotty is a library written in Go that determines and reads termcap database
|
||||
files to produce an interface for interacting with the capabilities of a
|
||||
terminal.
|
||||
See the godoc documentation or the source code for more information about
|
||||
function usage.
|
||||
3
cli/vendor/github.com/Nvveen/Gotty/TODO
generated
vendored
3
cli/vendor/github.com/Nvveen/Gotty/TODO
generated
vendored
@@ -1,3 +0,0 @@
|
||||
gotty.go:// TODO add more concurrency to name lookup, look for more opportunities.
|
||||
all:// TODO add more documentation, with function usage in a doc.go file.
|
||||
all:// TODO add more testing/benchmarking with go test.
|
||||
514
cli/vendor/github.com/Nvveen/Gotty/attributes.go
generated
vendored
514
cli/vendor/github.com/Nvveen/Gotty/attributes.go
generated
vendored
@@ -1,514 +0,0 @@
|
||||
// Copyright 2012 Neal van Veen. All rights reserved.
|
||||
// Usage of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package gotty
|
||||
|
||||
// Boolean capabilities
|
||||
var BoolAttr = [...]string{
|
||||
"auto_left_margin", "bw",
|
||||
"auto_right_margin", "am",
|
||||
"no_esc_ctlc", "xsb",
|
||||
"ceol_standout_glitch", "xhp",
|
||||
"eat_newline_glitch", "xenl",
|
||||
"erase_overstrike", "eo",
|
||||
"generic_type", "gn",
|
||||
"hard_copy", "hc",
|
||||
"has_meta_key", "km",
|
||||
"has_status_line", "hs",
|
||||
"insert_null_glitch", "in",
|
||||
"memory_above", "da",
|
||||
"memory_below", "db",
|
||||
"move_insert_mode", "mir",
|
||||
"move_standout_mode", "msgr",
|
||||
"over_strike", "os",
|
||||
"status_line_esc_ok", "eslok",
|
||||
"dest_tabs_magic_smso", "xt",
|
||||
"tilde_glitch", "hz",
|
||||
"transparent_underline", "ul",
|
||||
"xon_xoff", "nxon",
|
||||
"needs_xon_xoff", "nxon",
|
||||
"prtr_silent", "mc5i",
|
||||
"hard_cursor", "chts",
|
||||
"non_rev_rmcup", "nrrmc",
|
||||
"no_pad_char", "npc",
|
||||
"non_dest_scroll_region", "ndscr",
|
||||
"can_change", "ccc",
|
||||
"back_color_erase", "bce",
|
||||
"hue_lightness_saturation", "hls",
|
||||
"col_addr_glitch", "xhpa",
|
||||
"cr_cancels_micro_mode", "crxm",
|
||||
"has_print_wheel", "daisy",
|
||||
"row_addr_glitch", "xvpa",
|
||||
"semi_auto_right_margin", "sam",
|
||||
"cpi_changes_res", "cpix",
|
||||
"lpi_changes_res", "lpix",
|
||||
"backspaces_with_bs", "",
|
||||
"crt_no_scrolling", "",
|
||||
"no_correctly_working_cr", "",
|
||||
"gnu_has_meta_key", "",
|
||||
"linefeed_is_newline", "",
|
||||
"has_hardware_tabs", "",
|
||||
"return_does_clr_eol", "",
|
||||
}
|
||||
|
||||
// Numerical capabilities
|
||||
var NumAttr = [...]string{
|
||||
"columns", "cols",
|
||||
"init_tabs", "it",
|
||||
"lines", "lines",
|
||||
"lines_of_memory", "lm",
|
||||
"magic_cookie_glitch", "xmc",
|
||||
"padding_baud_rate", "pb",
|
||||
"virtual_terminal", "vt",
|
||||
"width_status_line", "wsl",
|
||||
"num_labels", "nlab",
|
||||
"label_height", "lh",
|
||||
"label_width", "lw",
|
||||
"max_attributes", "ma",
|
||||
"maximum_windows", "wnum",
|
||||
"max_colors", "colors",
|
||||
"max_pairs", "pairs",
|
||||
"no_color_video", "ncv",
|
||||
"buffer_capacity", "bufsz",
|
||||
"dot_vert_spacing", "spinv",
|
||||
"dot_horz_spacing", "spinh",
|
||||
"max_micro_address", "maddr",
|
||||
"max_micro_jump", "mjump",
|
||||
"micro_col_size", "mcs",
|
||||
"micro_line_size", "mls",
|
||||
"number_of_pins", "npins",
|
||||
"output_res_char", "orc",
|
||||
"output_res_line", "orl",
|
||||
"output_res_horz_inch", "orhi",
|
||||
"output_res_vert_inch", "orvi",
|
||||
"print_rate", "cps",
|
||||
"wide_char_size", "widcs",
|
||||
"buttons", "btns",
|
||||
"bit_image_entwining", "bitwin",
|
||||
"bit_image_type", "bitype",
|
||||
"magic_cookie_glitch_ul", "",
|
||||
"carriage_return_delay", "",
|
||||
"new_line_delay", "",
|
||||
"backspace_delay", "",
|
||||
"horizontal_tab_delay", "",
|
||||
"number_of_function_keys", "",
|
||||
}
|
||||
|
||||
// String capabilities
|
||||
var StrAttr = [...]string{
|
||||
"back_tab", "cbt",
|
||||
"bell", "bel",
|
||||
"carriage_return", "cr",
|
||||
"change_scroll_region", "csr",
|
||||
"clear_all_tabs", "tbc",
|
||||
"clear_screen", "clear",
|
||||
"clr_eol", "el",
|
||||
"clr_eos", "ed",
|
||||
"column_address", "hpa",
|
||||
"command_character", "cmdch",
|
||||
"cursor_address", "cup",
|
||||
"cursor_down", "cud1",
|
||||
"cursor_home", "home",
|
||||
"cursor_invisible", "civis",
|
||||
"cursor_left", "cub1",
|
||||
"cursor_mem_address", "mrcup",
|
||||
"cursor_normal", "cnorm",
|
||||
"cursor_right", "cuf1",
|
||||
"cursor_to_ll", "ll",
|
||||
"cursor_up", "cuu1",
|
||||
"cursor_visible", "cvvis",
|
||||
"delete_character", "dch1",
|
||||
"delete_line", "dl1",
|
||||
"dis_status_line", "dsl",
|
||||
"down_half_line", "hd",
|
||||
"enter_alt_charset_mode", "smacs",
|
||||
"enter_blink_mode", "blink",
|
||||
"enter_bold_mode", "bold",
|
||||
"enter_ca_mode", "smcup",
|
||||
"enter_delete_mode", "smdc",
|
||||
"enter_dim_mode", "dim",
|
||||
"enter_insert_mode", "smir",
|
||||
"enter_secure_mode", "invis",
|
||||
"enter_protected_mode", "prot",
|
||||
"enter_reverse_mode", "rev",
|
||||
"enter_standout_mode", "smso",
|
||||
"enter_underline_mode", "smul",
|
||||
"erase_chars", "ech",
|
||||
"exit_alt_charset_mode", "rmacs",
|
||||
"exit_attribute_mode", "sgr0",
|
||||
"exit_ca_mode", "rmcup",
|
||||
"exit_delete_mode", "rmdc",
|
||||
"exit_insert_mode", "rmir",
|
||||
"exit_standout_mode", "rmso",
|
||||
"exit_underline_mode", "rmul",
|
||||
"flash_screen", "flash",
|
||||
"form_feed", "ff",
|
||||
"from_status_line", "fsl",
|
||||
"init_1string", "is1",
|
||||
"init_2string", "is2",
|
||||
"init_3string", "is3",
|
||||
"init_file", "if",
|
||||
"insert_character", "ich1",
|
||||
"insert_line", "il1",
|
||||
"insert_padding", "ip",
|
||||
"key_backspace", "kbs",
|
||||
"key_catab", "ktbc",
|
||||
"key_clear", "kclr",
|
||||
"key_ctab", "kctab",
|
||||
"key_dc", "kdch1",
|
||||
"key_dl", "kdl1",
|
||||
"key_down", "kcud1",
|
||||
"key_eic", "krmir",
|
||||
"key_eol", "kel",
|
||||
"key_eos", "ked",
|
||||
"key_f0", "kf0",
|
||||
"key_f1", "kf1",
|
||||
"key_f10", "kf10",
|
||||
"key_f2", "kf2",
|
||||
"key_f3", "kf3",
|
||||
"key_f4", "kf4",
|
||||
"key_f5", "kf5",
|
||||
"key_f6", "kf6",
|
||||
"key_f7", "kf7",
|
||||
"key_f8", "kf8",
|
||||
"key_f9", "kf9",
|
||||
"key_home", "khome",
|
||||
"key_ic", "kich1",
|
||||
"key_il", "kil1",
|
||||
"key_left", "kcub1",
|
||||
"key_ll", "kll",
|
||||
"key_npage", "knp",
|
||||
"key_ppage", "kpp",
|
||||
"key_right", "kcuf1",
|
||||
"key_sf", "kind",
|
||||
"key_sr", "kri",
|
||||
"key_stab", "khts",
|
||||
"key_up", "kcuu1",
|
||||
"keypad_local", "rmkx",
|
||||
"keypad_xmit", "smkx",
|
||||
"lab_f0", "lf0",
|
||||
"lab_f1", "lf1",
|
||||
"lab_f10", "lf10",
|
||||
"lab_f2", "lf2",
|
||||
"lab_f3", "lf3",
|
||||
"lab_f4", "lf4",
|
||||
"lab_f5", "lf5",
|
||||
"lab_f6", "lf6",
|
||||
"lab_f7", "lf7",
|
||||
"lab_f8", "lf8",
|
||||
"lab_f9", "lf9",
|
||||
"meta_off", "rmm",
|
||||
"meta_on", "smm",
|
||||
"newline", "_glitch",
|
||||
"pad_char", "npc",
|
||||
"parm_dch", "dch",
|
||||
"parm_delete_line", "dl",
|
||||
"parm_down_cursor", "cud",
|
||||
"parm_ich", "ich",
|
||||
"parm_index", "indn",
|
||||
"parm_insert_line", "il",
|
||||
"parm_left_cursor", "cub",
|
||||
"parm_right_cursor", "cuf",
|
||||
"parm_rindex", "rin",
|
||||
"parm_up_cursor", "cuu",
|
||||
"pkey_key", "pfkey",
|
||||
"pkey_local", "pfloc",
|
||||
"pkey_xmit", "pfx",
|
||||
"print_screen", "mc0",
|
||||
"prtr_off", "mc4",
|
||||
"prtr_on", "mc5",
|
||||
"repeat_char", "rep",
|
||||
"reset_1string", "rs1",
|
||||
"reset_2string", "rs2",
|
||||
"reset_3string", "rs3",
|
||||
"reset_file", "rf",
|
||||
"restore_cursor", "rc",
|
||||
"row_address", "mvpa",
|
||||
"save_cursor", "row_address",
|
||||
"scroll_forward", "ind",
|
||||
"scroll_reverse", "ri",
|
||||
"set_attributes", "sgr",
|
||||
"set_tab", "hts",
|
||||
"set_window", "wind",
|
||||
"tab", "s_magic_smso",
|
||||
"to_status_line", "tsl",
|
||||
"underline_char", "uc",
|
||||
"up_half_line", "hu",
|
||||
"init_prog", "iprog",
|
||||
"key_a1", "ka1",
|
||||
"key_a3", "ka3",
|
||||
"key_b2", "kb2",
|
||||
"key_c1", "kc1",
|
||||
"key_c3", "kc3",
|
||||
"prtr_non", "mc5p",
|
||||
"char_padding", "rmp",
|
||||
"acs_chars", "acsc",
|
||||
"plab_norm", "pln",
|
||||
"key_btab", "kcbt",
|
||||
"enter_xon_mode", "smxon",
|
||||
"exit_xon_mode", "rmxon",
|
||||
"enter_am_mode", "smam",
|
||||
"exit_am_mode", "rmam",
|
||||
"xon_character", "xonc",
|
||||
"xoff_character", "xoffc",
|
||||
"ena_acs", "enacs",
|
||||
"label_on", "smln",
|
||||
"label_off", "rmln",
|
||||
"key_beg", "kbeg",
|
||||
"key_cancel", "kcan",
|
||||
"key_close", "kclo",
|
||||
"key_command", "kcmd",
|
||||
"key_copy", "kcpy",
|
||||
"key_create", "kcrt",
|
||||
"key_end", "kend",
|
||||
"key_enter", "kent",
|
||||
"key_exit", "kext",
|
||||
"key_find", "kfnd",
|
||||
"key_help", "khlp",
|
||||
"key_mark", "kmrk",
|
||||
"key_message", "kmsg",
|
||||
"key_move", "kmov",
|
||||
"key_next", "knxt",
|
||||
"key_open", "kopn",
|
||||
"key_options", "kopt",
|
||||
"key_previous", "kprv",
|
||||
"key_print", "kprt",
|
||||
"key_redo", "krdo",
|
||||
"key_reference", "kref",
|
||||
"key_refresh", "krfr",
|
||||
"key_replace", "krpl",
|
||||
"key_restart", "krst",
|
||||
"key_resume", "kres",
|
||||
"key_save", "ksav",
|
||||
"key_suspend", "kspd",
|
||||
"key_undo", "kund",
|
||||
"key_sbeg", "kBEG",
|
||||
"key_scancel", "kCAN",
|
||||
"key_scommand", "kCMD",
|
||||
"key_scopy", "kCPY",
|
||||
"key_screate", "kCRT",
|
||||
"key_sdc", "kDC",
|
||||
"key_sdl", "kDL",
|
||||
"key_select", "kslt",
|
||||
"key_send", "kEND",
|
||||
"key_seol", "kEOL",
|
||||
"key_sexit", "kEXT",
|
||||
"key_sfind", "kFND",
|
||||
"key_shelp", "kHLP",
|
||||
"key_shome", "kHOM",
|
||||
"key_sic", "kIC",
|
||||
"key_sleft", "kLFT",
|
||||
"key_smessage", "kMSG",
|
||||
"key_smove", "kMOV",
|
||||
"key_snext", "kNXT",
|
||||
"key_soptions", "kOPT",
|
||||
"key_sprevious", "kPRV",
|
||||
"key_sprint", "kPRT",
|
||||
"key_sredo", "kRDO",
|
||||
"key_sreplace", "kRPL",
|
||||
"key_sright", "kRIT",
|
||||
"key_srsume", "kRES",
|
||||
"key_ssave", "kSAV",
|
||||
"key_ssuspend", "kSPD",
|
||||
"key_sundo", "kUND",
|
||||
"req_for_input", "rfi",
|
||||
"key_f11", "kf11",
|
||||
"key_f12", "kf12",
|
||||
"key_f13", "kf13",
|
||||
"key_f14", "kf14",
|
||||
"key_f15", "kf15",
|
||||
"key_f16", "kf16",
|
||||
"key_f17", "kf17",
|
||||
"key_f18", "kf18",
|
||||
"key_f19", "kf19",
|
||||
"key_f20", "kf20",
|
||||
"key_f21", "kf21",
|
||||
"key_f22", "kf22",
|
||||
"key_f23", "kf23",
|
||||
"key_f24", "kf24",
|
||||
"key_f25", "kf25",
|
||||
"key_f26", "kf26",
|
||||
"key_f27", "kf27",
|
||||
"key_f28", "kf28",
|
||||
"key_f29", "kf29",
|
||||
"key_f30", "kf30",
|
||||
"key_f31", "kf31",
|
||||
"key_f32", "kf32",
|
||||
"key_f33", "kf33",
|
||||
"key_f34", "kf34",
|
||||
"key_f35", "kf35",
|
||||
"key_f36", "kf36",
|
||||
"key_f37", "kf37",
|
||||
"key_f38", "kf38",
|
||||
"key_f39", "kf39",
|
||||
"key_f40", "kf40",
|
||||
"key_f41", "kf41",
|
||||
"key_f42", "kf42",
|
||||
"key_f43", "kf43",
|
||||
"key_f44", "kf44",
|
||||
"key_f45", "kf45",
|
||||
"key_f46", "kf46",
|
||||
"key_f47", "kf47",
|
||||
"key_f48", "kf48",
|
||||
"key_f49", "kf49",
|
||||
"key_f50", "kf50",
|
||||
"key_f51", "kf51",
|
||||
"key_f52", "kf52",
|
||||
"key_f53", "kf53",
|
||||
"key_f54", "kf54",
|
||||
"key_f55", "kf55",
|
||||
"key_f56", "kf56",
|
||||
"key_f57", "kf57",
|
||||
"key_f58", "kf58",
|
||||
"key_f59", "kf59",
|
||||
"key_f60", "kf60",
|
||||
"key_f61", "kf61",
|
||||
"key_f62", "kf62",
|
||||
"key_f63", "kf63",
|
||||
"clr_bol", "el1",
|
||||
"clear_margins", "mgc",
|
||||
"set_left_margin", "smgl",
|
||||
"set_right_margin", "smgr",
|
||||
"label_format", "fln",
|
||||
"set_clock", "sclk",
|
||||
"display_clock", "dclk",
|
||||
"remove_clock", "rmclk",
|
||||
"create_window", "cwin",
|
||||
"goto_window", "wingo",
|
||||
"hangup", "hup",
|
||||
"dial_phone", "dial",
|
||||
"quick_dial", "qdial",
|
||||
"tone", "tone",
|
||||
"pulse", "pulse",
|
||||
"flash_hook", "hook",
|
||||
"fixed_pause", "pause",
|
||||
"wait_tone", "wait",
|
||||
"user0", "u0",
|
||||
"user1", "u1",
|
||||
"user2", "u2",
|
||||
"user3", "u3",
|
||||
"user4", "u4",
|
||||
"user5", "u5",
|
||||
"user6", "u6",
|
||||
"user7", "u7",
|
||||
"user8", "u8",
|
||||
"user9", "u9",
|
||||
"orig_pair", "op",
|
||||
"orig_colors", "oc",
|
||||
"initialize_color", "initc",
|
||||
"initialize_pair", "initp",
|
||||
"set_color_pair", "scp",
|
||||
"set_foreground", "setf",
|
||||
"set_background", "setb",
|
||||
"change_char_pitch", "cpi",
|
||||
"change_line_pitch", "lpi",
|
||||
"change_res_horz", "chr",
|
||||
"change_res_vert", "cvr",
|
||||
"define_char", "defc",
|
||||
"enter_doublewide_mode", "swidm",
|
||||
"enter_draft_quality", "sdrfq",
|
||||
"enter_italics_mode", "sitm",
|
||||
"enter_leftward_mode", "slm",
|
||||
"enter_micro_mode", "smicm",
|
||||
"enter_near_letter_quality", "snlq",
|
||||
"enter_normal_quality", "snrmq",
|
||||
"enter_shadow_mode", "sshm",
|
||||
"enter_subscript_mode", "ssubm",
|
||||
"enter_superscript_mode", "ssupm",
|
||||
"enter_upward_mode", "sum",
|
||||
"exit_doublewide_mode", "rwidm",
|
||||
"exit_italics_mode", "ritm",
|
||||
"exit_leftward_mode", "rlm",
|
||||
"exit_micro_mode", "rmicm",
|
||||
"exit_shadow_mode", "rshm",
|
||||
"exit_subscript_mode", "rsubm",
|
||||
"exit_superscript_mode", "rsupm",
|
||||
"exit_upward_mode", "rum",
|
||||
"micro_column_address", "mhpa",
|
||||
"micro_down", "mcud1",
|
||||
"micro_left", "mcub1",
|
||||
"micro_right", "mcuf1",
|
||||
"micro_row_address", "mvpa",
|
||||
"micro_up", "mcuu1",
|
||||
"order_of_pins", "porder",
|
||||
"parm_down_micro", "mcud",
|
||||
"parm_left_micro", "mcub",
|
||||
"parm_right_micro", "mcuf",
|
||||
"parm_up_micro", "mcuu",
|
||||
"select_char_set", "scs",
|
||||
"set_bottom_margin", "smgb",
|
||||
"set_bottom_margin_parm", "smgbp",
|
||||
"set_left_margin_parm", "smglp",
|
||||
"set_right_margin_parm", "smgrp",
|
||||
"set_top_margin", "smgt",
|
||||
"set_top_margin_parm", "smgtp",
|
||||
"start_bit_image", "sbim",
|
||||
"start_char_set_def", "scsd",
|
||||
"stop_bit_image", "rbim",
|
||||
"stop_char_set_def", "rcsd",
|
||||
"subscript_characters", "subcs",
|
||||
"superscript_characters", "supcs",
|
||||
"these_cause_cr", "docr",
|
||||
"zero_motion", "zerom",
|
||||
"char_set_names", "csnm",
|
||||
"key_mouse", "kmous",
|
||||
"mouse_info", "minfo",
|
||||
"req_mouse_pos", "reqmp",
|
||||
"get_mouse", "getm",
|
||||
"set_a_foreground", "setaf",
|
||||
"set_a_background", "setab",
|
||||
"pkey_plab", "pfxl",
|
||||
"device_type", "devt",
|
||||
"code_set_init", "csin",
|
||||
"set0_des_seq", "s0ds",
|
||||
"set1_des_seq", "s1ds",
|
||||
"set2_des_seq", "s2ds",
|
||||
"set3_des_seq", "s3ds",
|
||||
"set_lr_margin", "smglr",
|
||||
"set_tb_margin", "smgtb",
|
||||
"bit_image_repeat", "birep",
|
||||
"bit_image_newline", "binel",
|
||||
"bit_image_carriage_return", "bicr",
|
||||
"color_names", "colornm",
|
||||
"define_bit_image_region", "defbi",
|
||||
"end_bit_image_region", "endbi",
|
||||
"set_color_band", "setcolor",
|
||||
"set_page_length", "slines",
|
||||
"display_pc_char", "dispc",
|
||||
"enter_pc_charset_mode", "smpch",
|
||||
"exit_pc_charset_mode", "rmpch",
|
||||
"enter_scancode_mode", "smsc",
|
||||
"exit_scancode_mode", "rmsc",
|
||||
"pc_term_options", "pctrm",
|
||||
"scancode_escape", "scesc",
|
||||
"alt_scancode_esc", "scesa",
|
||||
"enter_horizontal_hl_mode", "ehhlm",
|
||||
"enter_left_hl_mode", "elhlm",
|
||||
"enter_low_hl_mode", "elohlm",
|
||||
"enter_right_hl_mode", "erhlm",
|
||||
"enter_top_hl_mode", "ethlm",
|
||||
"enter_vertical_hl_mode", "evhlm",
|
||||
"set_a_attributes", "sgr1",
|
||||
"set_pglen_inch", "slength",
|
||||
"termcap_init2", "",
|
||||
"termcap_reset", "",
|
||||
"linefeed_if_not_lf", "",
|
||||
"backspace_if_not_bs", "",
|
||||
"other_non_function_keys", "",
|
||||
"arrow_key_map", "",
|
||||
"acs_ulcorner", "",
|
||||
"acs_llcorner", "",
|
||||
"acs_urcorner", "",
|
||||
"acs_lrcorner", "",
|
||||
"acs_ltee", "",
|
||||
"acs_rtee", "",
|
||||
"acs_btee", "",
|
||||
"acs_ttee", "",
|
||||
"acs_hline", "",
|
||||
"acs_vline", "",
|
||||
"acs_plus", "",
|
||||
"memory_lock", "",
|
||||
"memory_unlock", "",
|
||||
"box_chars_1", "",
|
||||
}
|
||||
238
cli/vendor/github.com/Nvveen/Gotty/gotty.go
generated
vendored
238
cli/vendor/github.com/Nvveen/Gotty/gotty.go
generated
vendored
@@ -1,238 +0,0 @@
|
||||
// Copyright 2012 Neal van Veen. All rights reserved.
|
||||
// Usage of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Gotty is a Go-package for reading and parsing the terminfo database
|
||||
package gotty
|
||||
|
||||
// TODO add more concurrency to name lookup, look for more opportunities.
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Open a terminfo file by the name given and construct a TermInfo object.
|
||||
// If something went wrong reading the terminfo database file, an error is
|
||||
// returned.
|
||||
func OpenTermInfo(termName string) (*TermInfo, error) {
|
||||
var term *TermInfo
|
||||
var err error
|
||||
// Find the environment variables
|
||||
termloc := os.Getenv("TERMINFO")
|
||||
if len(termloc) == 0 {
|
||||
// Search like ncurses
|
||||
locations := []string{os.Getenv("HOME") + "/.terminfo/", "/etc/terminfo/",
|
||||
"/lib/terminfo/", "/usr/share/terminfo/"}
|
||||
var path string
|
||||
for _, str := range locations {
|
||||
// Construct path
|
||||
path = str + string(termName[0]) + "/" + termName
|
||||
// Check if path can be opened
|
||||
file, _ := os.Open(path)
|
||||
if file != nil {
|
||||
// Path can open, fall out and use current path
|
||||
file.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(path) > 0 {
|
||||
term, err = readTermInfo(path)
|
||||
} else {
|
||||
err = errors.New(fmt.Sprintf("No terminfo file(-location) found"))
|
||||
}
|
||||
}
|
||||
return term, err
|
||||
}
|
||||
|
||||
// Open a terminfo file from the environment variable containing the current
|
||||
// terminal name and construct a TermInfo object. If something went wrong
|
||||
// reading the terminfo database file, an error is returned.
|
||||
func OpenTermInfoEnv() (*TermInfo, error) {
|
||||
termenv := os.Getenv("TERM")
|
||||
return OpenTermInfo(termenv)
|
||||
}
|
||||
|
||||
// Return an attribute by the name attr provided. If none can be found,
|
||||
// an error is returned.
|
||||
func (term *TermInfo) GetAttribute(attr string) (stacker, error) {
|
||||
// Channel to store the main value in.
|
||||
var value stacker
|
||||
// Add a blocking WaitGroup
|
||||
var block sync.WaitGroup
|
||||
// Keep track of variable being written.
|
||||
written := false
|
||||
// Function to put into goroutine.
|
||||
f := func(ats interface{}) {
|
||||
var ok bool
|
||||
var v stacker
|
||||
// Switch on type of map to use and assign value to it.
|
||||
switch reflect.TypeOf(ats).Elem().Kind() {
|
||||
case reflect.Bool:
|
||||
v, ok = ats.(map[string]bool)[attr]
|
||||
case reflect.Int16:
|
||||
v, ok = ats.(map[string]int16)[attr]
|
||||
case reflect.String:
|
||||
v, ok = ats.(map[string]string)[attr]
|
||||
}
|
||||
// If ok, a value is found, so we can write.
|
||||
if ok {
|
||||
value = v
|
||||
written = true
|
||||
}
|
||||
// Goroutine is done
|
||||
block.Done()
|
||||
}
|
||||
block.Add(3)
|
||||
// Go for all 3 attribute lists.
|
||||
go f(term.boolAttributes)
|
||||
go f(term.numAttributes)
|
||||
go f(term.strAttributes)
|
||||
// Wait until every goroutine is done.
|
||||
block.Wait()
|
||||
// If a value has been written, return it.
|
||||
if written {
|
||||
return value, nil
|
||||
}
|
||||
// Otherwise, error.
|
||||
return nil, fmt.Errorf("Erorr finding attribute")
|
||||
}
|
||||
|
||||
// Return an attribute by the name attr provided. If none can be found,
|
||||
// an error is returned. A name is first converted to its termcap value.
|
||||
func (term *TermInfo) GetAttributeName(name string) (stacker, error) {
|
||||
tc := GetTermcapName(name)
|
||||
return term.GetAttribute(tc)
|
||||
}
|
||||
|
||||
// A utility function that finds and returns the termcap equivalent of a
|
||||
// variable name.
|
||||
func GetTermcapName(name string) string {
|
||||
// Termcap name
|
||||
var tc string
|
||||
// Blocking group
|
||||
var wait sync.WaitGroup
|
||||
// Function to put into a goroutine
|
||||
f := func(attrs []string) {
|
||||
// Find the string corresponding to the name
|
||||
for i, s := range attrs {
|
||||
if s == name {
|
||||
tc = attrs[i+1]
|
||||
}
|
||||
}
|
||||
// Goroutine is finished
|
||||
wait.Done()
|
||||
}
|
||||
wait.Add(3)
|
||||
// Go for all 3 attribute lists
|
||||
go f(BoolAttr[:])
|
||||
go f(NumAttr[:])
|
||||
go f(StrAttr[:])
|
||||
// Wait until every goroutine is done
|
||||
wait.Wait()
|
||||
// Return the termcap name
|
||||
return tc
|
||||
}
|
||||
|
||||
// This function takes a path to a terminfo file and reads it in binary
|
||||
// form to construct the actual TermInfo file.
|
||||
func readTermInfo(path string) (*TermInfo, error) {
|
||||
// Open the terminfo file
|
||||
file, err := os.Open(path)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// magic, nameSize, boolSize, nrSNum, nrOffsetsStr, strSize
|
||||
// Header is composed of the magic 0432 octal number, size of the name
|
||||
// section, size of the boolean section, the amount of number values,
|
||||
// the number of offsets of strings, and the size of the string section.
|
||||
var header [6]int16
|
||||
// Byte array is used to read in byte values
|
||||
var byteArray []byte
|
||||
// Short array is used to read in short values
|
||||
var shArray []int16
|
||||
// TermInfo object to store values
|
||||
var term TermInfo
|
||||
|
||||
// Read in the header
|
||||
err = binary.Read(file, binary.LittleEndian, &header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If magic number isn't there or isn't correct, we have the wrong filetype
|
||||
if header[0] != 0432 {
|
||||
return nil, errors.New(fmt.Sprintf("Wrong filetype"))
|
||||
}
|
||||
|
||||
// Read in the names
|
||||
byteArray = make([]byte, header[1])
|
||||
err = binary.Read(file, binary.LittleEndian, &byteArray)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
term.Names = strings.Split(string(byteArray), "|")
|
||||
|
||||
// Read in the booleans
|
||||
byteArray = make([]byte, header[2])
|
||||
err = binary.Read(file, binary.LittleEndian, &byteArray)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
term.boolAttributes = make(map[string]bool)
|
||||
for i, b := range byteArray {
|
||||
if b == 1 {
|
||||
term.boolAttributes[BoolAttr[i*2+1]] = true
|
||||
}
|
||||
}
|
||||
// If the number of bytes read is not even, a byte for alignment is added
|
||||
if len(byteArray)%2 != 0 {
|
||||
err = binary.Read(file, binary.LittleEndian, make([]byte, 1))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Read in shorts
|
||||
shArray = make([]int16, header[3])
|
||||
err = binary.Read(file, binary.LittleEndian, &shArray)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
term.numAttributes = make(map[string]int16)
|
||||
for i, n := range shArray {
|
||||
if n != 0377 && n > -1 {
|
||||
term.numAttributes[NumAttr[i*2+1]] = n
|
||||
}
|
||||
}
|
||||
|
||||
// Read the offsets into the short array
|
||||
shArray = make([]int16, header[4])
|
||||
err = binary.Read(file, binary.LittleEndian, &shArray)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Read the actual strings in the byte array
|
||||
byteArray = make([]byte, header[5])
|
||||
err = binary.Read(file, binary.LittleEndian, &byteArray)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
term.strAttributes = make(map[string]string)
|
||||
// We get an offset, and then iterate until the string is null-terminated
|
||||
for i, offset := range shArray {
|
||||
if offset > -1 {
|
||||
r := offset
|
||||
for ; byteArray[r] != 0; r++ {
|
||||
}
|
||||
term.strAttributes[StrAttr[i*2+1]] = string(byteArray[offset:r])
|
||||
}
|
||||
}
|
||||
return &term, nil
|
||||
}
|
||||
362
cli/vendor/github.com/Nvveen/Gotty/parser.go
generated
vendored
362
cli/vendor/github.com/Nvveen/Gotty/parser.go
generated
vendored
@@ -1,362 +0,0 @@
|
||||
// Copyright 2012 Neal van Veen. All rights reserved.
|
||||
// Usage of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package gotty
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var exp = [...]string{
|
||||
"%%",
|
||||
"%c",
|
||||
"%s",
|
||||
"%p(\\d)",
|
||||
"%P([A-z])",
|
||||
"%g([A-z])",
|
||||
"%'(.)'",
|
||||
"%{([0-9]+)}",
|
||||
"%l",
|
||||
"%\\+|%-|%\\*|%/|%m",
|
||||
"%&|%\\||%\\^",
|
||||
"%=|%>|%<",
|
||||
"%A|%O",
|
||||
"%!|%~",
|
||||
"%i",
|
||||
"%(:[\\ #\\-\\+]{0,4})?(\\d+\\.\\d+|\\d+)?[doxXs]",
|
||||
"%\\?(.*?);",
|
||||
}
|
||||
|
||||
var regex *regexp.Regexp
|
||||
var staticVar map[byte]stacker
|
||||
|
||||
// Parses the attribute that is received with name attr and parameters params.
|
||||
func (term *TermInfo) Parse(attr string, params ...interface{}) (string, error) {
|
||||
// Get the attribute name first.
|
||||
iface, err := term.GetAttribute(attr)
|
||||
str, ok := iface.(string)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !ok {
|
||||
return str, errors.New("Only string capabilities can be parsed.")
|
||||
}
|
||||
// Construct the hidden parser struct so we can use a recursive stack based
|
||||
// parser.
|
||||
ps := &parser{}
|
||||
// Dynamic variables only exist in this context.
|
||||
ps.dynamicVar = make(map[byte]stacker, 26)
|
||||
ps.parameters = make([]stacker, len(params))
|
||||
// Convert the parameters to insert them into the parser struct.
|
||||
for i, x := range params {
|
||||
ps.parameters[i] = x
|
||||
}
|
||||
// Recursively walk and return.
|
||||
result, err := ps.walk(str)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Parses the attribute that is received with name attr and parameters params.
|
||||
// Only works on full name of a capability that is given, which it uses to
|
||||
// search for the termcap name.
|
||||
func (term *TermInfo) ParseName(attr string, params ...interface{}) (string, error) {
|
||||
tc := GetTermcapName(attr)
|
||||
return term.Parse(tc, params)
|
||||
}
|
||||
|
||||
// Identify each token in a stack based manner and do the actual parsing.
|
||||
func (ps *parser) walk(attr string) (string, error) {
|
||||
// We use a buffer to get the modified string.
|
||||
var buf bytes.Buffer
|
||||
// Next, find and identify all tokens by their indices and strings.
|
||||
tokens := regex.FindAllStringSubmatch(attr, -1)
|
||||
if len(tokens) == 0 {
|
||||
return attr, nil
|
||||
}
|
||||
indices := regex.FindAllStringIndex(attr, -1)
|
||||
q := 0 // q counts the matches of one token
|
||||
// Iterate through the string per character.
|
||||
for i := 0; i < len(attr); i++ {
|
||||
// If the current position is an identified token, execute the following
|
||||
// steps.
|
||||
if q < len(indices) && i >= indices[q][0] && i < indices[q][1] {
|
||||
// Switch on token.
|
||||
switch {
|
||||
case tokens[q][0][:2] == "%%":
|
||||
// Literal percentage character.
|
||||
buf.WriteByte('%')
|
||||
case tokens[q][0][:2] == "%c":
|
||||
// Pop a character.
|
||||
c, err := ps.st.pop()
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
buf.WriteByte(c.(byte))
|
||||
case tokens[q][0][:2] == "%s":
|
||||
// Pop a string.
|
||||
str, err := ps.st.pop()
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
if _, ok := str.(string); !ok {
|
||||
return buf.String(), errors.New("Stack head is not a string")
|
||||
}
|
||||
buf.WriteString(str.(string))
|
||||
case tokens[q][0][:2] == "%p":
|
||||
// Push a parameter on the stack.
|
||||
index, err := strconv.ParseInt(tokens[q][1], 10, 8)
|
||||
index--
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
if int(index) >= len(ps.parameters) {
|
||||
return buf.String(), errors.New("Parameters index out of bound")
|
||||
}
|
||||
ps.st.push(ps.parameters[index])
|
||||
case tokens[q][0][:2] == "%P":
|
||||
// Pop a variable from the stack as a dynamic or static variable.
|
||||
val, err := ps.st.pop()
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
index := tokens[q][2]
|
||||
if len(index) > 1 {
|
||||
errorStr := fmt.Sprintf("%s is not a valid dynamic variables index",
|
||||
index)
|
||||
return buf.String(), errors.New(errorStr)
|
||||
}
|
||||
// Specify either dynamic or static.
|
||||
if index[0] >= 'a' && index[0] <= 'z' {
|
||||
ps.dynamicVar[index[0]] = val
|
||||
} else if index[0] >= 'A' && index[0] <= 'Z' {
|
||||
staticVar[index[0]] = val
|
||||
}
|
||||
case tokens[q][0][:2] == "%g":
|
||||
// Push a variable from the stack as a dynamic or static variable.
|
||||
index := tokens[q][3]
|
||||
if len(index) > 1 {
|
||||
errorStr := fmt.Sprintf("%s is not a valid static variables index",
|
||||
index)
|
||||
return buf.String(), errors.New(errorStr)
|
||||
}
|
||||
var val stacker
|
||||
if index[0] >= 'a' && index[0] <= 'z' {
|
||||
val = ps.dynamicVar[index[0]]
|
||||
} else if index[0] >= 'A' && index[0] <= 'Z' {
|
||||
val = staticVar[index[0]]
|
||||
}
|
||||
ps.st.push(val)
|
||||
case tokens[q][0][:2] == "%'":
|
||||
// Push a character constant.
|
||||
con := tokens[q][4]
|
||||
if len(con) > 1 {
|
||||
errorStr := fmt.Sprintf("%s is not a valid character constant", con)
|
||||
return buf.String(), errors.New(errorStr)
|
||||
}
|
||||
ps.st.push(con[0])
|
||||
case tokens[q][0][:2] == "%{":
|
||||
// Push an integer constant.
|
||||
con, err := strconv.ParseInt(tokens[q][5], 10, 32)
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
ps.st.push(con)
|
||||
case tokens[q][0][:2] == "%l":
|
||||
// Push the length of the string that is popped from the stack.
|
||||
popStr, err := ps.st.pop()
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
if _, ok := popStr.(string); !ok {
|
||||
errStr := fmt.Sprintf("Stack head is not a string")
|
||||
return buf.String(), errors.New(errStr)
|
||||
}
|
||||
ps.st.push(len(popStr.(string)))
|
||||
case tokens[q][0][:2] == "%?":
|
||||
// If-then-else construct. First, the whole string is identified and
|
||||
// then inside this substring, we can specify which parts to switch on.
|
||||
ifReg, _ := regexp.Compile("%\\?(.*)%t(.*)%e(.*);|%\\?(.*)%t(.*);")
|
||||
ifTokens := ifReg.FindStringSubmatch(tokens[q][0])
|
||||
var (
|
||||
ifStr string
|
||||
err error
|
||||
)
|
||||
// Parse the if-part to determine if-else.
|
||||
if len(ifTokens[1]) > 0 {
|
||||
ifStr, err = ps.walk(ifTokens[1])
|
||||
} else { // else
|
||||
ifStr, err = ps.walk(ifTokens[4])
|
||||
}
|
||||
// Return any errors
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
} else if len(ifStr) > 0 {
|
||||
// Self-defined limitation, not sure if this is correct, but didn't
|
||||
// seem like it.
|
||||
return buf.String(), errors.New("If-clause cannot print statements")
|
||||
}
|
||||
var thenStr string
|
||||
// Pop the first value that is set by parsing the if-clause.
|
||||
choose, err := ps.st.pop()
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
// Switch to if or else.
|
||||
if choose.(int) == 0 && len(ifTokens[1]) > 0 {
|
||||
thenStr, err = ps.walk(ifTokens[3])
|
||||
} else if choose.(int) != 0 {
|
||||
if len(ifTokens[1]) > 0 {
|
||||
thenStr, err = ps.walk(ifTokens[2])
|
||||
} else {
|
||||
thenStr, err = ps.walk(ifTokens[5])
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
buf.WriteString(thenStr)
|
||||
case tokens[q][0][len(tokens[q][0])-1] == 'd': // Fallthrough for printing
|
||||
fallthrough
|
||||
case tokens[q][0][len(tokens[q][0])-1] == 'o': // digits.
|
||||
fallthrough
|
||||
case tokens[q][0][len(tokens[q][0])-1] == 'x':
|
||||
fallthrough
|
||||
case tokens[q][0][len(tokens[q][0])-1] == 'X':
|
||||
fallthrough
|
||||
case tokens[q][0][len(tokens[q][0])-1] == 's':
|
||||
token := tokens[q][0]
|
||||
// Remove the : that comes before a flag.
|
||||
if token[1] == ':' {
|
||||
token = token[:1] + token[2:]
|
||||
}
|
||||
digit, err := ps.st.pop()
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
// The rest is determined like the normal formatted prints.
|
||||
digitStr := fmt.Sprintf(token, digit.(int))
|
||||
buf.WriteString(digitStr)
|
||||
case tokens[q][0][:2] == "%i":
|
||||
// Increment the parameters by one.
|
||||
if len(ps.parameters) < 2 {
|
||||
return buf.String(), errors.New("Not enough parameters to increment.")
|
||||
}
|
||||
val1, val2 := ps.parameters[0].(int), ps.parameters[1].(int)
|
||||
val1++
|
||||
val2++
|
||||
ps.parameters[0], ps.parameters[1] = val1, val2
|
||||
default:
|
||||
// The rest of the tokens is a special case, where two values are
|
||||
// popped and then operated on by the token that comes after them.
|
||||
op1, err := ps.st.pop()
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
op2, err := ps.st.pop()
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
var result stacker
|
||||
switch tokens[q][0][:2] {
|
||||
case "%+":
|
||||
// Addition
|
||||
result = op2.(int) + op1.(int)
|
||||
case "%-":
|
||||
// Subtraction
|
||||
result = op2.(int) - op1.(int)
|
||||
case "%*":
|
||||
// Multiplication
|
||||
result = op2.(int) * op1.(int)
|
||||
case "%/":
|
||||
// Division
|
||||
result = op2.(int) / op1.(int)
|
||||
case "%m":
|
||||
// Modulo
|
||||
result = op2.(int) % op1.(int)
|
||||
case "%&":
|
||||
// Bitwise AND
|
||||
result = op2.(int) & op1.(int)
|
||||
case "%|":
|
||||
// Bitwise OR
|
||||
result = op2.(int) | op1.(int)
|
||||
case "%^":
|
||||
// Bitwise XOR
|
||||
result = op2.(int) ^ op1.(int)
|
||||
case "%=":
|
||||
// Equals
|
||||
result = op2 == op1
|
||||
case "%>":
|
||||
// Greater-than
|
||||
result = op2.(int) > op1.(int)
|
||||
case "%<":
|
||||
// Lesser-than
|
||||
result = op2.(int) < op1.(int)
|
||||
case "%A":
|
||||
// Logical AND
|
||||
result = op2.(bool) && op1.(bool)
|
||||
case "%O":
|
||||
// Logical OR
|
||||
result = op2.(bool) || op1.(bool)
|
||||
case "%!":
|
||||
// Logical complement
|
||||
result = !op1.(bool)
|
||||
case "%~":
|
||||
// Bitwise complement
|
||||
result = ^(op1.(int))
|
||||
}
|
||||
ps.st.push(result)
|
||||
}
|
||||
|
||||
i = indices[q][1] - 1
|
||||
q++
|
||||
} else {
|
||||
// We are not "inside" a token, so just skip until the end or the next
|
||||
// token, and add all characters to the buffer.
|
||||
j := i
|
||||
if q != len(indices) {
|
||||
for !(j >= indices[q][0] && j < indices[q][1]) {
|
||||
j++
|
||||
}
|
||||
} else {
|
||||
j = len(attr)
|
||||
}
|
||||
buf.WriteString(string(attr[i:j]))
|
||||
i = j
|
||||
}
|
||||
}
|
||||
// Return the buffer as a string.
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// Push a stacker-value onto the stack.
|
||||
func (st *stack) push(s stacker) {
|
||||
*st = append(*st, s)
|
||||
}
|
||||
|
||||
// Pop a stacker-value from the stack.
|
||||
func (st *stack) pop() (stacker, error) {
|
||||
if len(*st) == 0 {
|
||||
return nil, errors.New("Stack is empty.")
|
||||
}
|
||||
newStack := make(stack, len(*st)-1)
|
||||
val := (*st)[len(*st)-1]
|
||||
copy(newStack, (*st)[:len(*st)-1])
|
||||
*st = newStack
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// Initialize regexes and the static vars (that don't get changed between
|
||||
// calls.
|
||||
func init() {
|
||||
// Initialize the main regex.
|
||||
expStr := strings.Join(exp[:], "|")
|
||||
regex, _ = regexp.Compile(expStr)
|
||||
// Initialize the static variables.
|
||||
staticVar = make(map[byte]stacker, 26)
|
||||
}
|
||||
23
cli/vendor/github.com/Nvveen/Gotty/types.go
generated
vendored
23
cli/vendor/github.com/Nvveen/Gotty/types.go
generated
vendored
@@ -1,23 +0,0 @@
|
||||
// Copyright 2012 Neal van Veen. All rights reserved.
|
||||
// Usage of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package gotty
|
||||
|
||||
type TermInfo struct {
|
||||
boolAttributes map[string]bool
|
||||
numAttributes map[string]int16
|
||||
strAttributes map[string]string
|
||||
// The various names of the TermInfo file.
|
||||
Names []string
|
||||
}
|
||||
|
||||
type stacker interface {
|
||||
}
|
||||
type stack []stacker
|
||||
|
||||
type parser struct {
|
||||
st stack
|
||||
parameters []stacker
|
||||
dynamicVar map[byte]stacker
|
||||
}
|
||||
5
cli/vendor/github.com/PuerkitoBio/purell/.gitignore
generated
vendored
5
cli/vendor/github.com/PuerkitoBio/purell/.gitignore
generated
vendored
@@ -1,5 +0,0 @@
|
||||
*.sublime-*
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.swo
|
||||
tags
|
||||
9
cli/vendor/github.com/PuerkitoBio/purell/.travis.yml
generated
vendored
9
cli/vendor/github.com/PuerkitoBio/purell/.travis.yml
generated
vendored
@@ -1,9 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4.x
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- tip
|
||||
12
cli/vendor/github.com/PuerkitoBio/purell/LICENSE
generated
vendored
12
cli/vendor/github.com/PuerkitoBio/purell/LICENSE
generated
vendored
@@ -1,12 +0,0 @@
|
||||
Copyright (c) 2012, Martin Angers
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
187
cli/vendor/github.com/PuerkitoBio/purell/README.md
generated
vendored
187
cli/vendor/github.com/PuerkitoBio/purell/README.md
generated
vendored
@@ -1,187 +0,0 @@
|
||||
# Purell
|
||||
|
||||
Purell is a tiny Go library to normalize URLs. It returns a pure URL. Pure-ell. Sanitizer and all. Yeah, I know...
|
||||
|
||||
Based on the [wikipedia paper][wiki] and the [RFC 3986 document][rfc].
|
||||
|
||||
[](http://travis-ci.org/PuerkitoBio/purell)
|
||||
|
||||
## Install
|
||||
|
||||
`go get github.com/PuerkitoBio/purell`
|
||||
|
||||
## Changelog
|
||||
|
||||
* **2016-11-14 (v1.1.0)** : IDN: Conform to RFC 5895: Fold character width (thanks to @beeker1121).
|
||||
* **2016-07-27 (v1.0.0)** : Normalize IDN to ASCII (thanks to @zenovich).
|
||||
* **2015-02-08** : Add fix for relative paths issue ([PR #5][pr5]) and add fix for unnecessary encoding of reserved characters ([see issue #7][iss7]).
|
||||
* **v0.2.0** : Add benchmarks, Attempt IDN support.
|
||||
* **v0.1.0** : Initial release.
|
||||
|
||||
## Examples
|
||||
|
||||
From `example_test.go` (note that in your code, you would import "github.com/PuerkitoBio/purell", and would prefix references to its methods and constants with "purell."):
|
||||
|
||||
```go
|
||||
package purell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func ExampleNormalizeURLString() {
|
||||
if normalized, err := NormalizeURLString("hTTp://someWEBsite.com:80/Amazing%3f/url/",
|
||||
FlagLowercaseScheme|FlagLowercaseHost|FlagUppercaseEscapes); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
fmt.Print(normalized)
|
||||
}
|
||||
// Output: http://somewebsite.com:80/Amazing%3F/url/
|
||||
}
|
||||
|
||||
func ExampleMustNormalizeURLString() {
|
||||
normalized := MustNormalizeURLString("hTTpS://someWEBsite.com:443/Amazing%fa/url/",
|
||||
FlagsUnsafeGreedy)
|
||||
fmt.Print(normalized)
|
||||
|
||||
// Output: http://somewebsite.com/Amazing%FA/url
|
||||
}
|
||||
|
||||
func ExampleNormalizeURL() {
|
||||
if u, err := url.Parse("Http://SomeUrl.com:8080/a/b/.././c///g?c=3&a=1&b=9&c=0#target"); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
normalized := NormalizeURL(u, FlagsUsuallySafeGreedy|FlagRemoveDuplicateSlashes|FlagRemoveFragment)
|
||||
fmt.Print(normalized)
|
||||
}
|
||||
|
||||
// Output: http://someurl.com:8080/a/c/g?c=3&a=1&b=9&c=0
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
As seen in the examples above, purell offers three methods, `NormalizeURLString(string, NormalizationFlags) (string, error)`, `MustNormalizeURLString(string, NormalizationFlags) (string)` and `NormalizeURL(*url.URL, NormalizationFlags) (string)`. They all normalize the provided URL based on the specified flags. Here are the available flags:
|
||||
|
||||
```go
|
||||
const (
|
||||
// Safe normalizations
|
||||
FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1
|
||||
FlagLowercaseHost // http://HOST -> http://host
|
||||
FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF
|
||||
FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA
|
||||
FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$
|
||||
FlagRemoveDefaultPort // http://host:80 -> http://host
|
||||
FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path
|
||||
|
||||
// Usually safe normalizations
|
||||
FlagRemoveTrailingSlash // http://host/path/ -> http://host/path
|
||||
FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags)
|
||||
FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c
|
||||
|
||||
// Unsafe normalizations
|
||||
FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/
|
||||
FlagRemoveFragment // http://host/path#fragment -> http://host/path
|
||||
FlagForceHTTP // https://host -> http://host
|
||||
FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b
|
||||
FlagRemoveWWW // http://www.host/ -> http://host/
|
||||
FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags)
|
||||
FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3
|
||||
|
||||
// Normalizations not in the wikipedia article, required to cover tests cases
|
||||
// submitted by jehiah
|
||||
FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147
|
||||
FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147
|
||||
FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147
|
||||
FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path
|
||||
FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path
|
||||
|
||||
// Convenience set of safe normalizations
|
||||
FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator
|
||||
|
||||
// For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags,
|
||||
// while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix".
|
||||
|
||||
// Convenience set of usually safe normalizations (includes FlagsSafe)
|
||||
FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments
|
||||
FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments
|
||||
|
||||
// Convenience set of unsafe normalizations (includes FlagsUsuallySafe)
|
||||
FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery
|
||||
FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery
|
||||
|
||||
// Convenience set of all available flags
|
||||
FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
||||
FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
||||
)
|
||||
```
|
||||
|
||||
For convenience, the set of flags `FlagsSafe`, `FlagsUsuallySafe[Greedy|NonGreedy]`, `FlagsUnsafe[Greedy|NonGreedy]` and `FlagsAll[Greedy|NonGreedy]` are provided for the similarly grouped normalizations on [wikipedia's URL normalization page][wiki]. You can add (using the bitwise OR `|` operator) or remove (using the bitwise AND NOT `&^` operator) individual flags from the sets if required, to build your own custom set.
|
||||
|
||||
The [full godoc reference is available on gopkgdoc][godoc].
|
||||
|
||||
Some things to note:
|
||||
|
||||
* `FlagDecodeUnnecessaryEscapes`, `FlagEncodeNecessaryEscapes`, `FlagUppercaseEscapes` and `FlagRemoveEmptyQuerySeparator` are always implicitly set, because internally, the URL string is parsed as an URL object, which automatically decodes unnecessary escapes, uppercases and encodes necessary ones, and removes empty query separators (an unnecessary `?` at the end of the url). So this operation cannot **not** be done. For this reason, `FlagRemoveEmptyQuerySeparator` (as well as the other three) has been included in the `FlagsSafe` convenience set, instead of `FlagsUnsafe`, where Wikipedia puts it.
|
||||
|
||||
* The `FlagDecodeUnnecessaryEscapes` decodes the following escapes (*from -> to*):
|
||||
- %24 -> $
|
||||
- %26 -> &
|
||||
- %2B-%3B -> +,-./0123456789:;
|
||||
- %3D -> =
|
||||
- %40-%5A -> @ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
- %5F -> _
|
||||
- %61-%7A -> abcdefghijklmnopqrstuvwxyz
|
||||
- %7E -> ~
|
||||
|
||||
|
||||
* When the `NormalizeURL` function is used (passing an URL object), this source URL object is modified (that is, after the call, the URL object will be modified to reflect the normalization).
|
||||
|
||||
* The *replace IP with domain name* normalization (`http://208.77.188.166/ → http://www.example.com/`) is obviously not possible for a library without making some network requests. This is not implemented in purell.
|
||||
|
||||
* The *remove unused query string parameters* and *remove default query parameters* are also not implemented, since this is a very case-specific normalization, and it is quite trivial to do with an URL object.
|
||||
|
||||
### Safe vs Usually Safe vs Unsafe
|
||||
|
||||
Purell allows you to control the level of risk you take while normalizing an URL. You can aggressively normalize, play it totally safe, or anything in between.
|
||||
|
||||
Consider the following URL:
|
||||
|
||||
`HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid`
|
||||
|
||||
Normalizing with the `FlagsSafe` gives:
|
||||
|
||||
`https://www.root.com/toto/tE%1F///a/./b/../c/?z=3&w=2&a=4&w=1#invalid`
|
||||
|
||||
With the `FlagsUsuallySafeGreedy`:
|
||||
|
||||
`https://www.root.com/toto/tE%1F///a/c?z=3&w=2&a=4&w=1#invalid`
|
||||
|
||||
And with `FlagsUnsafeGreedy`:
|
||||
|
||||
`http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3`
|
||||
|
||||
## TODOs
|
||||
|
||||
* Add a class/default instance to allow specifying custom directory index names? At the moment, removing directory index removes `(^|/)((?:default|index)\.\w{1,4})$`.
|
||||
|
||||
## Thanks / Contributions
|
||||
|
||||
@rogpeppe
|
||||
@jehiah
|
||||
@opennota
|
||||
@pchristopher1275
|
||||
@zenovich
|
||||
@beeker1121
|
||||
|
||||
## License
|
||||
|
||||
The [BSD 3-Clause license][bsd].
|
||||
|
||||
[bsd]: http://opensource.org/licenses/BSD-3-Clause
|
||||
[wiki]: http://en.wikipedia.org/wiki/URL_normalization
|
||||
[rfc]: http://tools.ietf.org/html/rfc3986#section-6
|
||||
[godoc]: http://go.pkgdoc.org/github.com/PuerkitoBio/purell
|
||||
[pr5]: https://github.com/PuerkitoBio/purell/pull/5
|
||||
[iss7]: https://github.com/PuerkitoBio/purell/issues/7
|
||||
57
cli/vendor/github.com/PuerkitoBio/purell/bench_test.go
generated
vendored
57
cli/vendor/github.com/PuerkitoBio/purell/bench_test.go
generated
vendored
@@ -1,57 +0,0 @@
|
||||
package purell
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
safeUrl = "HttPS://..iaMHost..Test:443/paTh^A%ef//./%41PaTH/..//?"
|
||||
usuallySafeUrl = "HttPS://..iaMHost..Test:443/paTh^A%ef//./%41PaTH/../final/"
|
||||
unsafeUrl = "HttPS://..www.iaMHost..Test:443/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment"
|
||||
allDWORDUrl = "HttPS://1113982867:/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment"
|
||||
allOctalUrl = "HttPS://0102.0146.07.0223:/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment"
|
||||
allHexUrl = "HttPS://0x42660793:/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment"
|
||||
allCombinedUrl = "HttPS://..0x42660793.:/paTh^A%ef//./%41PaTH/../final/index.html?t=val1&a=val4&z=val5&a=val1#fragment"
|
||||
)
|
||||
|
||||
func BenchmarkSafe(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
NormalizeURLString(safeUrl, FlagsSafe)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUsuallySafe(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
NormalizeURLString(usuallySafeUrl, FlagsUsuallySafeGreedy)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnsafe(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
NormalizeURLString(unsafeUrl, FlagsUnsafeGreedy)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAllDWORD(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
NormalizeURLString(allDWORDUrl, FlagsAllGreedy)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAllOctal(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
NormalizeURLString(allOctalUrl, FlagsAllGreedy)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAllHex(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
NormalizeURLString(allHexUrl, FlagsAllGreedy)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAllCombined(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
NormalizeURLString(allCombinedUrl, FlagsAllGreedy)
|
||||
}
|
||||
}
|
||||
9
cli/vendor/github.com/PuerkitoBio/purell/benchmarks/v0.1.0
generated
vendored
9
cli/vendor/github.com/PuerkitoBio/purell/benchmarks/v0.1.0
generated
vendored
@@ -1,9 +0,0 @@
|
||||
PASS
|
||||
BenchmarkSafe 500000 6131 ns/op
|
||||
BenchmarkUsuallySafe 200000 7864 ns/op
|
||||
BenchmarkUnsafe 100000 28560 ns/op
|
||||
BenchmarkAllDWORD 50000 38722 ns/op
|
||||
BenchmarkAllOctal 50000 40941 ns/op
|
||||
BenchmarkAllHex 50000 44063 ns/op
|
||||
BenchmarkAllCombined 50000 33613 ns/op
|
||||
ok github.com/PuerkitoBio/purell 17.404s
|
||||
35
cli/vendor/github.com/PuerkitoBio/purell/example_test.go
generated
vendored
35
cli/vendor/github.com/PuerkitoBio/purell/example_test.go
generated
vendored
@@ -1,35 +0,0 @@
|
||||
package purell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func ExampleNormalizeURLString() {
|
||||
if normalized, err := NormalizeURLString("hTTp://someWEBsite.com:80/Amazing%3f/url/",
|
||||
FlagLowercaseScheme|FlagLowercaseHost|FlagUppercaseEscapes); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
fmt.Print(normalized)
|
||||
}
|
||||
// Output: http://somewebsite.com:80/Amazing%3F/url/
|
||||
}
|
||||
|
||||
func ExampleMustNormalizeURLString() {
|
||||
normalized := MustNormalizeURLString("hTTpS://someWEBsite.com:443/Amazing%fa/url/",
|
||||
FlagsUnsafeGreedy)
|
||||
fmt.Print(normalized)
|
||||
|
||||
// Output: http://somewebsite.com/Amazing%FA/url
|
||||
}
|
||||
|
||||
func ExampleNormalizeURL() {
|
||||
if u, err := url.Parse("Http://SomeUrl.com:8080/a/b/.././c///g?c=3&a=1&b=9&c=0#target"); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
normalized := NormalizeURL(u, FlagsUsuallySafeGreedy|FlagRemoveDuplicateSlashes|FlagRemoveFragment)
|
||||
fmt.Print(normalized)
|
||||
}
|
||||
|
||||
// Output: http://someurl.com:8080/a/c/g?c=3&a=1&b=9&c=0
|
||||
}
|
||||
379
cli/vendor/github.com/PuerkitoBio/purell/purell.go
generated
vendored
379
cli/vendor/github.com/PuerkitoBio/purell/purell.go
generated
vendored
@@ -1,379 +0,0 @@
|
||||
/*
|
||||
Package purell offers URL normalization as described on the wikipedia page:
|
||||
http://en.wikipedia.org/wiki/URL_normalization
|
||||
*/
|
||||
package purell
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/urlesc"
|
||||
"golang.org/x/net/idna"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
"golang.org/x/text/width"
|
||||
)
|
||||
|
||||
// A set of normalization flags determines how a URL will
|
||||
// be normalized.
|
||||
type NormalizationFlags uint
|
||||
|
||||
const (
|
||||
// Safe normalizations
|
||||
FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1
|
||||
FlagLowercaseHost // http://HOST -> http://host
|
||||
FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF
|
||||
FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA
|
||||
FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$
|
||||
FlagRemoveDefaultPort // http://host:80 -> http://host
|
||||
FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path
|
||||
|
||||
// Usually safe normalizations
|
||||
FlagRemoveTrailingSlash // http://host/path/ -> http://host/path
|
||||
FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags)
|
||||
FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c
|
||||
|
||||
// Unsafe normalizations
|
||||
FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/
|
||||
FlagRemoveFragment // http://host/path#fragment -> http://host/path
|
||||
FlagForceHTTP // https://host -> http://host
|
||||
FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b
|
||||
FlagRemoveWWW // http://www.host/ -> http://host/
|
||||
FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags)
|
||||
FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3
|
||||
|
||||
// Normalizations not in the wikipedia article, required to cover tests cases
|
||||
// submitted by jehiah
|
||||
FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147
|
||||
FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147
|
||||
FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147
|
||||
FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path
|
||||
FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path
|
||||
|
||||
// Convenience set of safe normalizations
|
||||
FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator
|
||||
|
||||
// For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags,
|
||||
// while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix".
|
||||
|
||||
// Convenience set of usually safe normalizations (includes FlagsSafe)
|
||||
FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments
|
||||
FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments
|
||||
|
||||
// Convenience set of unsafe normalizations (includes FlagsUsuallySafe)
|
||||
FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery
|
||||
FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery
|
||||
|
||||
// Convenience set of all available flags
|
||||
FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
||||
FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
||||
)
|
||||
|
||||
const (
|
||||
defaultHttpPort = ":80"
|
||||
defaultHttpsPort = ":443"
|
||||
)
|
||||
|
||||
// Regular expressions used by the normalizations
|
||||
var rxPort = regexp.MustCompile(`(:\d+)/?$`)
|
||||
var rxDirIndex = regexp.MustCompile(`(^|/)((?:default|index)\.\w{1,4})$`)
|
||||
var rxDupSlashes = regexp.MustCompile(`/{2,}`)
|
||||
var rxDWORDHost = regexp.MustCompile(`^(\d+)((?:\.+)?(?:\:\d*)?)$`)
|
||||
var rxOctalHost = regexp.MustCompile(`^(0\d*)\.(0\d*)\.(0\d*)\.(0\d*)((?:\.+)?(?:\:\d*)?)$`)
|
||||
var rxHexHost = regexp.MustCompile(`^0x([0-9A-Fa-f]+)((?:\.+)?(?:\:\d*)?)$`)
|
||||
var rxHostDots = regexp.MustCompile(`^(.+?)(:\d+)?$`)
|
||||
var rxEmptyPort = regexp.MustCompile(`:+$`)
|
||||
|
||||
// Map of flags to implementation function.
|
||||
// FlagDecodeUnnecessaryEscapes has no action, since it is done automatically
|
||||
// by parsing the string as an URL. Same for FlagUppercaseEscapes and FlagRemoveEmptyQuerySeparator.
|
||||
|
||||
// Since maps have undefined traversing order, make a slice of ordered keys
|
||||
var flagsOrder = []NormalizationFlags{
|
||||
FlagLowercaseScheme,
|
||||
FlagLowercaseHost,
|
||||
FlagRemoveDefaultPort,
|
||||
FlagRemoveDirectoryIndex,
|
||||
FlagRemoveDotSegments,
|
||||
FlagRemoveFragment,
|
||||
FlagForceHTTP, // Must be after remove default port (because https=443/http=80)
|
||||
FlagRemoveDuplicateSlashes,
|
||||
FlagRemoveWWW,
|
||||
FlagAddWWW,
|
||||
FlagSortQuery,
|
||||
FlagDecodeDWORDHost,
|
||||
FlagDecodeOctalHost,
|
||||
FlagDecodeHexHost,
|
||||
FlagRemoveUnnecessaryHostDots,
|
||||
FlagRemoveEmptyPortSeparator,
|
||||
FlagRemoveTrailingSlash, // These two (add/remove trailing slash) must be last
|
||||
FlagAddTrailingSlash,
|
||||
}
|
||||
|
||||
// ... and then the map, where order is unimportant
|
||||
var flags = map[NormalizationFlags]func(*url.URL){
|
||||
FlagLowercaseScheme: lowercaseScheme,
|
||||
FlagLowercaseHost: lowercaseHost,
|
||||
FlagRemoveDefaultPort: removeDefaultPort,
|
||||
FlagRemoveDirectoryIndex: removeDirectoryIndex,
|
||||
FlagRemoveDotSegments: removeDotSegments,
|
||||
FlagRemoveFragment: removeFragment,
|
||||
FlagForceHTTP: forceHTTP,
|
||||
FlagRemoveDuplicateSlashes: removeDuplicateSlashes,
|
||||
FlagRemoveWWW: removeWWW,
|
||||
FlagAddWWW: addWWW,
|
||||
FlagSortQuery: sortQuery,
|
||||
FlagDecodeDWORDHost: decodeDWORDHost,
|
||||
FlagDecodeOctalHost: decodeOctalHost,
|
||||
FlagDecodeHexHost: decodeHexHost,
|
||||
FlagRemoveUnnecessaryHostDots: removeUnncessaryHostDots,
|
||||
FlagRemoveEmptyPortSeparator: removeEmptyPortSeparator,
|
||||
FlagRemoveTrailingSlash: removeTrailingSlash,
|
||||
FlagAddTrailingSlash: addTrailingSlash,
|
||||
}
|
||||
|
||||
// MustNormalizeURLString returns the normalized string, and panics if an error occurs.
|
||||
// It takes an URL string as input, as well as the normalization flags.
|
||||
func MustNormalizeURLString(u string, f NormalizationFlags) string {
|
||||
result, e := NormalizeURLString(u, f)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// NormalizeURLString returns the normalized string, or an error if it can't be parsed into an URL object.
|
||||
// It takes an URL string as input, as well as the normalization flags.
|
||||
func NormalizeURLString(u string, f NormalizationFlags) (string, error) {
|
||||
parsed, err := url.Parse(u)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if f&FlagLowercaseHost == FlagLowercaseHost {
|
||||
parsed.Host = strings.ToLower(parsed.Host)
|
||||
}
|
||||
|
||||
// The idna package doesn't fully conform to RFC 5895
|
||||
// (https://tools.ietf.org/html/rfc5895), so we do it here.
|
||||
// Taken from Go 1.8 cycle source, courtesy of bradfitz.
|
||||
// TODO: Remove when (if?) idna package conforms to RFC 5895.
|
||||
parsed.Host = width.Fold.String(parsed.Host)
|
||||
parsed.Host = norm.NFC.String(parsed.Host)
|
||||
if parsed.Host, err = idna.ToASCII(parsed.Host); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return NormalizeURL(parsed, f), nil
|
||||
}
|
||||
|
||||
// NormalizeURL returns the normalized string.
|
||||
// It takes a parsed URL object as input, as well as the normalization flags.
|
||||
func NormalizeURL(u *url.URL, f NormalizationFlags) string {
|
||||
for _, k := range flagsOrder {
|
||||
if f&k == k {
|
||||
flags[k](u)
|
||||
}
|
||||
}
|
||||
return urlesc.Escape(u)
|
||||
}
|
||||
|
||||
func lowercaseScheme(u *url.URL) {
|
||||
if len(u.Scheme) > 0 {
|
||||
u.Scheme = strings.ToLower(u.Scheme)
|
||||
}
|
||||
}
|
||||
|
||||
func lowercaseHost(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
u.Host = strings.ToLower(u.Host)
|
||||
}
|
||||
}
|
||||
|
||||
func removeDefaultPort(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
scheme := strings.ToLower(u.Scheme)
|
||||
u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string {
|
||||
if (scheme == "http" && val == defaultHttpPort) || (scheme == "https" && val == defaultHttpsPort) {
|
||||
return ""
|
||||
}
|
||||
return val
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func removeTrailingSlash(u *url.URL) {
|
||||
if l := len(u.Path); l > 0 {
|
||||
if strings.HasSuffix(u.Path, "/") {
|
||||
u.Path = u.Path[:l-1]
|
||||
}
|
||||
} else if l = len(u.Host); l > 0 {
|
||||
if strings.HasSuffix(u.Host, "/") {
|
||||
u.Host = u.Host[:l-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addTrailingSlash(u *url.URL) {
|
||||
if l := len(u.Path); l > 0 {
|
||||
if !strings.HasSuffix(u.Path, "/") {
|
||||
u.Path += "/"
|
||||
}
|
||||
} else if l = len(u.Host); l > 0 {
|
||||
if !strings.HasSuffix(u.Host, "/") {
|
||||
u.Host += "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeDotSegments(u *url.URL) {
|
||||
if len(u.Path) > 0 {
|
||||
var dotFree []string
|
||||
var lastIsDot bool
|
||||
|
||||
sections := strings.Split(u.Path, "/")
|
||||
for _, s := range sections {
|
||||
if s == ".." {
|
||||
if len(dotFree) > 0 {
|
||||
dotFree = dotFree[:len(dotFree)-1]
|
||||
}
|
||||
} else if s != "." {
|
||||
dotFree = append(dotFree, s)
|
||||
}
|
||||
lastIsDot = (s == "." || s == "..")
|
||||
}
|
||||
// Special case if host does not end with / and new path does not begin with /
|
||||
u.Path = strings.Join(dotFree, "/")
|
||||
if u.Host != "" && !strings.HasSuffix(u.Host, "/") && !strings.HasPrefix(u.Path, "/") {
|
||||
u.Path = "/" + u.Path
|
||||
}
|
||||
// Special case if the last segment was a dot, make sure the path ends with a slash
|
||||
if lastIsDot && !strings.HasSuffix(u.Path, "/") {
|
||||
u.Path += "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeDirectoryIndex(u *url.URL) {
|
||||
if len(u.Path) > 0 {
|
||||
u.Path = rxDirIndex.ReplaceAllString(u.Path, "$1")
|
||||
}
|
||||
}
|
||||
|
||||
func removeFragment(u *url.URL) {
|
||||
u.Fragment = ""
|
||||
}
|
||||
|
||||
func forceHTTP(u *url.URL) {
|
||||
if strings.ToLower(u.Scheme) == "https" {
|
||||
u.Scheme = "http"
|
||||
}
|
||||
}
|
||||
|
||||
func removeDuplicateSlashes(u *url.URL) {
|
||||
if len(u.Path) > 0 {
|
||||
u.Path = rxDupSlashes.ReplaceAllString(u.Path, "/")
|
||||
}
|
||||
}
|
||||
|
||||
func removeWWW(u *url.URL) {
|
||||
if len(u.Host) > 0 && strings.HasPrefix(strings.ToLower(u.Host), "www.") {
|
||||
u.Host = u.Host[4:]
|
||||
}
|
||||
}
|
||||
|
||||
func addWWW(u *url.URL) {
|
||||
if len(u.Host) > 0 && !strings.HasPrefix(strings.ToLower(u.Host), "www.") {
|
||||
u.Host = "www." + u.Host
|
||||
}
|
||||
}
|
||||
|
||||
func sortQuery(u *url.URL) {
|
||||
q := u.Query()
|
||||
|
||||
if len(q) > 0 {
|
||||
arKeys := make([]string, len(q))
|
||||
i := 0
|
||||
for k, _ := range q {
|
||||
arKeys[i] = k
|
||||
i++
|
||||
}
|
||||
sort.Strings(arKeys)
|
||||
buf := new(bytes.Buffer)
|
||||
for _, k := range arKeys {
|
||||
sort.Strings(q[k])
|
||||
for _, v := range q[k] {
|
||||
if buf.Len() > 0 {
|
||||
buf.WriteRune('&')
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("%s=%s", k, urlesc.QueryEscape(v)))
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild the raw query string
|
||||
u.RawQuery = buf.String()
|
||||
}
|
||||
}
|
||||
|
||||
func decodeDWORDHost(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
if matches := rxDWORDHost.FindStringSubmatch(u.Host); len(matches) > 2 {
|
||||
var parts [4]int64
|
||||
|
||||
dword, _ := strconv.ParseInt(matches[1], 10, 0)
|
||||
for i, shift := range []uint{24, 16, 8, 0} {
|
||||
parts[i] = dword >> shift & 0xFF
|
||||
}
|
||||
u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeOctalHost(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
if matches := rxOctalHost.FindStringSubmatch(u.Host); len(matches) > 5 {
|
||||
var parts [4]int64
|
||||
|
||||
for i := 1; i <= 4; i++ {
|
||||
parts[i-1], _ = strconv.ParseInt(matches[i], 8, 0)
|
||||
}
|
||||
u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[5])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeHexHost(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
if matches := rxHexHost.FindStringSubmatch(u.Host); len(matches) > 2 {
|
||||
// Conversion is safe because of regex validation
|
||||
parsed, _ := strconv.ParseInt(matches[1], 16, 0)
|
||||
// Set host as DWORD (base 10) encoded host
|
||||
u.Host = fmt.Sprintf("%d%s", parsed, matches[2])
|
||||
// The rest is the same as decoding a DWORD host
|
||||
decodeDWORDHost(u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeUnncessaryHostDots(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
if matches := rxHostDots.FindStringSubmatch(u.Host); len(matches) > 1 {
|
||||
// Trim the leading and trailing dots
|
||||
u.Host = strings.Trim(matches[1], ".")
|
||||
if len(matches) > 2 {
|
||||
u.Host += matches[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeEmptyPortSeparator(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
u.Host = rxEmptyPort.ReplaceAllString(u.Host, "")
|
||||
}
|
||||
}
|
||||
768
cli/vendor/github.com/PuerkitoBio/purell/purell_test.go
generated
vendored
768
cli/vendor/github.com/PuerkitoBio/purell/purell_test.go
generated
vendored
@@ -1,768 +0,0 @@
|
||||
package purell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testCase struct {
|
||||
nm string
|
||||
src string
|
||||
flgs NormalizationFlags
|
||||
res string
|
||||
parsed bool
|
||||
}
|
||||
|
||||
var (
|
||||
cases = [...]*testCase{
|
||||
&testCase{
|
||||
"LowerScheme",
|
||||
"HTTP://www.SRC.ca",
|
||||
FlagLowercaseScheme,
|
||||
"http://www.SRC.ca",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"LowerScheme2",
|
||||
"http://www.SRC.ca",
|
||||
FlagLowercaseScheme,
|
||||
"http://www.SRC.ca",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"LowerHost",
|
||||
"HTTP://www.SRC.ca/",
|
||||
FlagLowercaseHost,
|
||||
"http://www.src.ca/", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"UpperEscapes",
|
||||
`http://www.whatever.com/Some%aa%20Special%8Ecases/`,
|
||||
FlagUppercaseEscapes,
|
||||
"http://www.whatever.com/Some%AA%20Special%8Ecases/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"UnnecessaryEscapes",
|
||||
`http://www.toto.com/%41%42%2E%44/%32%33%52%2D/%5f%7E`,
|
||||
FlagDecodeUnnecessaryEscapes,
|
||||
"http://www.toto.com/AB.D/23R-/_~",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveDefaultPort",
|
||||
"HTTP://www.SRC.ca:80/",
|
||||
FlagRemoveDefaultPort,
|
||||
"http://www.SRC.ca/", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveDefaultPort2",
|
||||
"HTTP://www.SRC.ca:80",
|
||||
FlagRemoveDefaultPort,
|
||||
"http://www.SRC.ca", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveDefaultPort3",
|
||||
"HTTP://www.SRC.ca:8080",
|
||||
FlagRemoveDefaultPort,
|
||||
"http://www.SRC.ca:8080", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Safe",
|
||||
"HTTP://www.SRC.ca:80/to%1ato%8b%ee/OKnow%41%42%43%7e",
|
||||
FlagsSafe,
|
||||
"http://www.src.ca/to%1Ato%8B%EE/OKnowABC~",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"BothLower",
|
||||
"HTTP://www.SRC.ca:80/to%1ato%8b%ee/OKnow%41%42%43%7e",
|
||||
FlagLowercaseHost | FlagLowercaseScheme,
|
||||
"http://www.src.ca:80/to%1Ato%8B%EE/OKnowABC~",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveTrailingSlash",
|
||||
"HTTP://www.SRC.ca:80/",
|
||||
FlagRemoveTrailingSlash,
|
||||
"http://www.SRC.ca:80", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveTrailingSlash2",
|
||||
"HTTP://www.SRC.ca:80/toto/titi/",
|
||||
FlagRemoveTrailingSlash,
|
||||
"http://www.SRC.ca:80/toto/titi", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveTrailingSlash3",
|
||||
"HTTP://www.SRC.ca:80/toto/titi/fin/?a=1",
|
||||
FlagRemoveTrailingSlash,
|
||||
"http://www.SRC.ca:80/toto/titi/fin?a=1", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"AddTrailingSlash",
|
||||
"HTTP://www.SRC.ca:80",
|
||||
FlagAddTrailingSlash,
|
||||
"http://www.SRC.ca:80/", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"AddTrailingSlash2",
|
||||
"HTTP://www.SRC.ca:80/toto/titi.html",
|
||||
FlagAddTrailingSlash,
|
||||
"http://www.SRC.ca:80/toto/titi.html/", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"AddTrailingSlash3",
|
||||
"HTTP://www.SRC.ca:80/toto/titi/fin?a=1",
|
||||
FlagAddTrailingSlash,
|
||||
"http://www.SRC.ca:80/toto/titi/fin/?a=1", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveDotSegments",
|
||||
"HTTP://root/a/b/./../../c/",
|
||||
FlagRemoveDotSegments,
|
||||
"http://root/c/", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveDotSegments2",
|
||||
"HTTP://root/../a/b/./../c/../d",
|
||||
FlagRemoveDotSegments,
|
||||
"http://root/a/d", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"UsuallySafe",
|
||||
"HTTP://www.SRC.ca:80/to%1ato%8b%ee/./c/d/../OKnow%41%42%43%7e/?a=b#test",
|
||||
FlagsUsuallySafeGreedy,
|
||||
"http://www.src.ca/to%1Ato%8B%EE/c/OKnowABC~?a=b#test",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveDirectoryIndex",
|
||||
"HTTP://root/a/b/c/default.aspx",
|
||||
FlagRemoveDirectoryIndex,
|
||||
"http://root/a/b/c/", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveDirectoryIndex2",
|
||||
"HTTP://root/a/b/c/default#a=b",
|
||||
FlagRemoveDirectoryIndex,
|
||||
"http://root/a/b/c/default#a=b", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveFragment",
|
||||
"HTTP://root/a/b/c/default#toto=tata",
|
||||
FlagRemoveFragment,
|
||||
"http://root/a/b/c/default", // Since Go1.1, scheme is automatically lowercased
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"ForceHTTP",
|
||||
"https://root/a/b/c/default#toto=tata",
|
||||
FlagForceHTTP,
|
||||
"http://root/a/b/c/default#toto=tata",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveDuplicateSlashes",
|
||||
"https://root/a//b///c////default#toto=tata",
|
||||
FlagRemoveDuplicateSlashes,
|
||||
"https://root/a/b/c/default#toto=tata",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveDuplicateSlashes2",
|
||||
"https://root//a//b///c////default#toto=tata",
|
||||
FlagRemoveDuplicateSlashes,
|
||||
"https://root/a/b/c/default#toto=tata",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveWWW",
|
||||
"https://www.root/a/b/c/",
|
||||
FlagRemoveWWW,
|
||||
"https://root/a/b/c/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveWWW2",
|
||||
"https://WwW.Root/a/b/c/",
|
||||
FlagRemoveWWW,
|
||||
"https://Root/a/b/c/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"AddWWW",
|
||||
"https://Root/a/b/c/",
|
||||
FlagAddWWW,
|
||||
"https://www.Root/a/b/c/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"SortQuery",
|
||||
"http://root/toto/?b=4&a=1&c=3&b=2&a=5",
|
||||
FlagSortQuery,
|
||||
"http://root/toto/?a=1&a=5&b=2&b=4&c=3",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"RemoveEmptyQuerySeparator",
|
||||
"http://root/toto/?",
|
||||
FlagRemoveEmptyQuerySeparator,
|
||||
"http://root/toto/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Unsafe",
|
||||
"HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
|
||||
FlagsUnsafeGreedy,
|
||||
"http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Safe2",
|
||||
"HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
|
||||
FlagsSafe,
|
||||
"https://www.root.com/toto/tE%1F///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"UsuallySafe2",
|
||||
"HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
|
||||
FlagsUsuallySafeGreedy,
|
||||
"https://www.root.com/toto/tE%1F///a/c?z=3&w=2&a=4&w=1#invalid",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"AddTrailingSlashBug",
|
||||
"http://src.ca/",
|
||||
FlagsAllNonGreedy,
|
||||
"http://www.src.ca/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"SourceModified",
|
||||
"HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid",
|
||||
FlagsUnsafeGreedy,
|
||||
"http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3",
|
||||
true,
|
||||
},
|
||||
&testCase{
|
||||
"IPv6-1",
|
||||
"http://[2001:db8:1f70::999:de8:7648:6e8]/test",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://[2001:db8:1f70::999:de8:7648:6e8]/test",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"IPv6-2",
|
||||
"http://[::ffff:192.168.1.1]/test",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://[::ffff:192.168.1.1]/test",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"IPv6-3",
|
||||
"http://[::ffff:192.168.1.1]:80/test",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://[::ffff:192.168.1.1]/test",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"IPv6-4",
|
||||
"htTps://[::fFff:192.168.1.1]:443/test",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"https://[::ffff:192.168.1.1]/test",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"FTP",
|
||||
"ftp://user:pass@ftp.foo.net/foo/bar",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"ftp://user:pass@ftp.foo.net/foo/bar",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Standard-1",
|
||||
"http://www.foo.com:80/foo",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://www.foo.com/foo",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Standard-2",
|
||||
"http://www.foo.com:8000/foo",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://www.foo.com:8000/foo",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Standard-3",
|
||||
"http://www.foo.com/%7ebar",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://www.foo.com/~bar",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Standard-4",
|
||||
"http://www.foo.com/%7Ebar",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://www.foo.com/~bar",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Standard-5",
|
||||
"http://USER:pass@www.Example.COM/foo/bar",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://USER:pass@www.example.com/foo/bar",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Standard-6",
|
||||
"http://test.example/?a=%26&b=1",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://test.example/?a=%26&b=1",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Standard-7",
|
||||
"http://test.example/%25/?p=%20val%20%25",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://test.example/%25/?p=%20val%20%25",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Standard-8",
|
||||
"http://test.example/path/with a%20space+/",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://test.example/path/with%20a%20space+/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Standard-9",
|
||||
"http://test.example/?",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://test.example/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Standard-10",
|
||||
"http://a.COM/path/?b&a",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://a.com/path/?b&a",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"StandardCasesAddTrailingSlash",
|
||||
"http://test.example?",
|
||||
FlagsSafe | FlagAddTrailingSlash,
|
||||
"http://test.example/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"OctalIP-1",
|
||||
"http://0123.011.0.4/",
|
||||
FlagsSafe | FlagDecodeOctalHost,
|
||||
"http://0123.011.0.4/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"OctalIP-2",
|
||||
"http://0102.0146.07.0223/",
|
||||
FlagsSafe | FlagDecodeOctalHost,
|
||||
"http://66.102.7.147/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"OctalIP-3",
|
||||
"http://0102.0146.07.0223.:23/",
|
||||
FlagsSafe | FlagDecodeOctalHost,
|
||||
"http://66.102.7.147.:23/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"OctalIP-4",
|
||||
"http://USER:pass@0102.0146.07.0223../",
|
||||
FlagsSafe | FlagDecodeOctalHost,
|
||||
"http://USER:pass@66.102.7.147../",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"DWORDIP-1",
|
||||
"http://123.1113982867/",
|
||||
FlagsSafe | FlagDecodeDWORDHost,
|
||||
"http://123.1113982867/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"DWORDIP-2",
|
||||
"http://1113982867/",
|
||||
FlagsSafe | FlagDecodeDWORDHost,
|
||||
"http://66.102.7.147/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"DWORDIP-3",
|
||||
"http://1113982867.:23/",
|
||||
FlagsSafe | FlagDecodeDWORDHost,
|
||||
"http://66.102.7.147.:23/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"DWORDIP-4",
|
||||
"http://USER:pass@1113982867../",
|
||||
FlagsSafe | FlagDecodeDWORDHost,
|
||||
"http://USER:pass@66.102.7.147../",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"HexIP-1",
|
||||
"http://0x123.1113982867/",
|
||||
FlagsSafe | FlagDecodeHexHost,
|
||||
"http://0x123.1113982867/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"HexIP-2",
|
||||
"http://0x42660793/",
|
||||
FlagsSafe | FlagDecodeHexHost,
|
||||
"http://66.102.7.147/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"HexIP-3",
|
||||
"http://0x42660793.:23/",
|
||||
FlagsSafe | FlagDecodeHexHost,
|
||||
"http://66.102.7.147.:23/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"HexIP-4",
|
||||
"http://USER:pass@0x42660793../",
|
||||
FlagsSafe | FlagDecodeHexHost,
|
||||
"http://USER:pass@66.102.7.147../",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"UnnecessaryHostDots-1",
|
||||
"http://.www.foo.com../foo/bar.html",
|
||||
FlagsSafe | FlagRemoveUnnecessaryHostDots,
|
||||
"http://www.foo.com/foo/bar.html",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"UnnecessaryHostDots-2",
|
||||
"http://www.foo.com./foo/bar.html",
|
||||
FlagsSafe | FlagRemoveUnnecessaryHostDots,
|
||||
"http://www.foo.com/foo/bar.html",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"UnnecessaryHostDots-3",
|
||||
"http://www.foo.com.:81/foo",
|
||||
FlagsSafe | FlagRemoveUnnecessaryHostDots,
|
||||
"http://www.foo.com:81/foo",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"UnnecessaryHostDots-4",
|
||||
"http://www.example.com./",
|
||||
FlagsSafe | FlagRemoveUnnecessaryHostDots,
|
||||
"http://www.example.com/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"EmptyPort-1",
|
||||
"http://www.thedraymin.co.uk:/main/?p=308",
|
||||
FlagsSafe | FlagRemoveEmptyPortSeparator,
|
||||
"http://www.thedraymin.co.uk/main/?p=308",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"EmptyPort-2",
|
||||
"http://www.src.ca:",
|
||||
FlagsSafe | FlagRemoveEmptyPortSeparator,
|
||||
"http://www.src.ca",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-1",
|
||||
"http://test.example/foo/bar/.",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo/bar/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-2",
|
||||
"http://test.example/foo/bar/./",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo/bar/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-3",
|
||||
"http://test.example/foo/bar/..",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-4",
|
||||
"http://test.example/foo/bar/../",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-5",
|
||||
"http://test.example/foo/bar/../baz",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo/baz",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-6",
|
||||
"http://test.example/foo/bar/../..",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-7",
|
||||
"http://test.example/foo/bar/../../",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-8",
|
||||
"http://test.example/foo/bar/../../baz",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/baz",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-9",
|
||||
"http://test.example/foo/bar/../../../baz",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/baz",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-10",
|
||||
"http://test.example/foo/bar/../../../../baz",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/baz",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-11",
|
||||
"http://test.example/./foo",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-12",
|
||||
"http://test.example/../foo",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-13",
|
||||
"http://test.example/foo.",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo.",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-14",
|
||||
"http://test.example/.foo",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/.foo",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-15",
|
||||
"http://test.example/foo..",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo..",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-16",
|
||||
"http://test.example/..foo",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/..foo",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-17",
|
||||
"http://test.example/./../foo",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-18",
|
||||
"http://test.example/./foo/.",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-19",
|
||||
"http://test.example/foo/./bar",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo/bar",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-20",
|
||||
"http://test.example/foo/../bar",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/bar",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-21",
|
||||
"http://test.example/foo//",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Slashes-22",
|
||||
"http://test.example/foo///bar//",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"http://test.example/foo/bar/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Relative",
|
||||
"foo/bar",
|
||||
FlagsAllGreedy,
|
||||
"foo/bar",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Relative-1",
|
||||
"./../foo",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"foo",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Relative-2",
|
||||
"./foo/bar/../baz/../bang/..",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"foo/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Relative-3",
|
||||
"foo///bar//",
|
||||
FlagsSafe | FlagRemoveDotSegments | FlagRemoveDuplicateSlashes,
|
||||
"foo/bar/",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"Relative-4",
|
||||
"www.youtube.com",
|
||||
FlagsUsuallySafeGreedy,
|
||||
"www.youtube.com",
|
||||
false,
|
||||
},
|
||||
/*&testCase{
|
||||
"UrlNorm-5",
|
||||
"http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%82%BF%E3%83%94%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%91%E3%83%B3",
|
||||
FlagsSafe | FlagRemoveDotSegments,
|
||||
"http://ja.wikipedia.org/wiki/\xe3\x82\xad\xe3\x83\xa3\xe3\x82\xbf\xe3\x83\x94\xe3\x83\xa9\xe3\x83\xbc\xe3\x82\xb8\xe3\x83\xa3\xe3\x83\x91\xe3\x83\xb3",
|
||||
false,
|
||||
},
|
||||
&testCase{
|
||||
"UrlNorm-1",
|
||||
"http://test.example/?a=%e3%82%82%26",
|
||||
FlagsAllGreedy,
|
||||
"http://test.example/?a=\xe3\x82\x82%26",
|
||||
false,
|
||||
},*/
|
||||
}
|
||||
)
|
||||
|
||||
func TestRunner(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
runCase(tc, t)
|
||||
}
|
||||
}
|
||||
|
||||
func runCase(tc *testCase, t *testing.T) {
|
||||
t.Logf("running %s...", tc.nm)
|
||||
if tc.parsed {
|
||||
u, e := url.Parse(tc.src)
|
||||
if e != nil {
|
||||
t.Errorf("%s - FAIL : %s", tc.nm, e)
|
||||
return
|
||||
} else {
|
||||
NormalizeURL(u, tc.flgs)
|
||||
if s := u.String(); s != tc.res {
|
||||
t.Errorf("%s - FAIL expected '%s', got '%s'", tc.nm, tc.res, s)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if s, e := NormalizeURLString(tc.src, tc.flgs); e != nil {
|
||||
t.Errorf("%s - FAIL : %s", tc.nm, e)
|
||||
} else if s != tc.res {
|
||||
t.Errorf("%s - FAIL expected '%s', got '%s'", tc.nm, tc.res, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeUnnecessaryEscapesAll(t *testing.T) {
|
||||
var url = "http://host/"
|
||||
|
||||
for i := 0; i < 256; i++ {
|
||||
url += fmt.Sprintf("%%%02x", i)
|
||||
}
|
||||
if s, e := NormalizeURLString(url, FlagDecodeUnnecessaryEscapes); e != nil {
|
||||
t.Fatalf("Got error %s", e.Error())
|
||||
} else {
|
||||
const want = "http://host/%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20!%22%23$%25&'()*+,-./0123456789:;%3C=%3E%3F@ABCDEFGHIJKLMNOPQRSTUVWXYZ[%5C]%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%7F%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF"
|
||||
if s != want {
|
||||
t.Errorf("DecodeUnnecessaryEscapesAll:\nwant\n%s\ngot\n%s", want, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeNecessaryEscapesAll(t *testing.T) {
|
||||
var url = "http://host/"
|
||||
|
||||
for i := 0; i < 256; i++ {
|
||||
if i != 0x25 {
|
||||
url += string(i)
|
||||
}
|
||||
}
|
||||
if s, e := NormalizeURLString(url, FlagEncodeNecessaryEscapes); e != nil {
|
||||
t.Fatalf("Got error %s", e.Error())
|
||||
} else {
|
||||
const want = "http://host/%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20!%22#$&'()*+,-./0123456789:;%3C=%3E?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[%5C]%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%7F%C2%80%C2%81%C2%82%C2%83%C2%84%C2%85%C2%86%C2%87%C2%88%C2%89%C2%8A%C2%8B%C2%8C%C2%8D%C2%8E%C2%8F%C2%90%C2%91%C2%92%C2%93%C2%94%C2%95%C2%96%C2%97%C2%98%C2%99%C2%9A%C2%9B%C2%9C%C2%9D%C2%9E%C2%9F%C2%A0%C2%A1%C2%A2%C2%A3%C2%A4%C2%A5%C2%A6%C2%A7%C2%A8%C2%A9%C2%AA%C2%AB%C2%AC%C2%AD%C2%AE%C2%AF%C2%B0%C2%B1%C2%B2%C2%B3%C2%B4%C2%B5%C2%B6%C2%B7%C2%B8%C2%B9%C2%BA%C2%BB%C2%BC%C2%BD%C2%BE%C2%BF%C3%80%C3%81%C3%82%C3%83%C3%84%C3%85%C3%86%C3%87%C3%88%C3%89%C3%8A%C3%8B%C3%8C%C3%8D%C3%8E%C3%8F%C3%90%C3%91%C3%92%C3%93%C3%94%C3%95%C3%96%C3%97%C3%98%C3%99%C3%9A%C3%9B%C3%9C%C3%9D%C3%9E%C3%9F%C3%A0%C3%A1%C3%A2%C3%A3%C3%A4%C3%A5%C3%A6%C3%A7%C3%A8%C3%A9%C3%AA%C3%AB%C3%AC%C3%AD%C3%AE%C3%AF%C3%B0%C3%B1%C3%B2%C3%B3%C3%B4%C3%B5%C3%B6%C3%B7%C3%B8%C3%B9%C3%BA%C3%BB%C3%BC%C3%BD%C3%BE%C3%BF"
|
||||
if s != want {
|
||||
t.Errorf("EncodeNecessaryEscapesAll:\nwant\n%s\ngot\n%s", want, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
53
cli/vendor/github.com/PuerkitoBio/purell/urlnorm_test.go
generated
vendored
53
cli/vendor/github.com/PuerkitoBio/purell/urlnorm_test.go
generated
vendored
@@ -1,53 +0,0 @@
|
||||
package purell
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test cases merged from PR #1
|
||||
// Originally from https://github.com/jehiah/urlnorm/blob/master/test_urlnorm.py
|
||||
|
||||
func assertMap(t *testing.T, cases map[string]string, f NormalizationFlags) {
|
||||
for bad, good := range cases {
|
||||
s, e := NormalizeURLString(bad, f)
|
||||
if e != nil {
|
||||
t.Errorf("%s normalizing %v to %v", e.Error(), bad, good)
|
||||
} else {
|
||||
if s != good {
|
||||
t.Errorf("source: %v expected: %v got: %v", bad, good, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This tests normalization to a unicode representation
|
||||
// precent escapes for unreserved values are unescaped to their unicode value
|
||||
// tests normalization to idna domains
|
||||
// test ip word handling, ipv6 address handling, and trailing domain periods
|
||||
// in general, this matches google chromes unescaping for things in the address bar.
|
||||
// spaces are converted to '+' (perhaphs controversial)
|
||||
// http://code.google.com/p/google-url/ probably is another good reference for this approach
|
||||
func TestUrlnorm(t *testing.T) {
|
||||
testcases := map[string]string{
|
||||
"http://test.example/?a=%e3%82%82%26": "http://test.example/?a=%e3%82%82%26",
|
||||
//"http://test.example/?a=%e3%82%82%26": "http://test.example/?a=\xe3\x82\x82%26", //should return a unicode character
|
||||
"http://s.xn--q-bga.DE/": "http://s.xn--q-bga.de/", //should be in idna format
|
||||
"http://XBLA\u306eXbox.com": "http://xn--xblaxbox-jf4g.com", //test utf8 and unicode
|
||||
"http://президент.рф": "http://xn--d1abbgf6aiiy.xn--p1ai",
|
||||
"http://ПРЕЗИДЕНТ.РФ": "http://xn--d1abbgf6aiiy.xn--p1ai",
|
||||
"http://ab¥ヲ₩○.com": "http://xn--ab-ida8983azmfnvs.com", //test width folding
|
||||
"http://\u00e9.com": "http://xn--9ca.com",
|
||||
"http://e\u0301.com": "http://xn--9ca.com",
|
||||
"http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%82%BF%E3%83%94%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%91%E3%83%B3": "http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%82%BF%E3%83%94%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%91%E3%83%B3",
|
||||
//"http://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%82%BF%E3%83%94%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%91%E3%83%B3": "http://ja.wikipedia.org/wiki/\xe3\x82\xad\xe3\x83\xa3\xe3\x82\xbf\xe3\x83\x94\xe3\x83\xa9\xe3\x83\xbc\xe3\x82\xb8\xe3\x83\xa3\xe3\x83\x91\xe3\x83\xb3",
|
||||
|
||||
"http://test.example/\xe3\x82\xad": "http://test.example/%E3%82%AD",
|
||||
//"http://test.example/\xe3\x82\xad": "http://test.example/\xe3\x82\xad",
|
||||
"http://test.example/?p=%23val#test-%23-val%25": "http://test.example/?p=%23val#test-%23-val%25", //check that %23 (#) is not escaped where it shouldn't be
|
||||
|
||||
"http://test.domain/I%C3%B1t%C3%ABrn%C3%A2ti%C3%B4n%EF%BF%BDliz%C3%A6ti%C3%B8n": "http://test.domain/I%C3%B1t%C3%ABrn%C3%A2ti%C3%B4n%EF%BF%BDliz%C3%A6ti%C3%B8n",
|
||||
//"http://test.domain/I%C3%B1t%C3%ABrn%C3%A2ti%C3%B4n%EF%BF%BDliz%C3%A6ti%C3%B8n": "http://test.domain/I\xc3\xb1t\xc3\xabrn\xc3\xa2ti\xc3\xb4n\xef\xbf\xbdliz\xc3\xa6ti\xc3\xb8n",
|
||||
}
|
||||
|
||||
assertMap(t, testcases, FlagsSafe|FlagRemoveDotSegments)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user