-
Notifications
You must be signed in to change notification settings - Fork 162
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
scion: one command to rule them all (#3708)
Cobra has shown to be a very useful framework for writing CLI applications. It makes it easy to write applications with many sub-commands and high configurability. We currently have many binaries in the code base, that do not really warrant to be their own application. With the approach that we put most application logic in `go/pkg/...`, we can easily create standalone binaries (when needed/desired) and also bundle them in an all-in-one scion command. This PR is a exploration how this could look like. At the base is the `scion` command that bundles all the other commands. For this exploration, I moved the showpaths logic to `go/pkgs/showpaths` such that it can be imported as a library. The old `showpaths` binary is not affected by the change, and if we continue to support the standalone binary, this will be easy to do. Under `go/scion` the all-in-on binary is set up. Currently, it only has 3 commands: - `completion` : generate auto-completion - `version`: display the scion version - `showpaths`: run the showpaths application The nice thing is, that we gradually can add sub-commands that make sense to be bundled. E.g., I envision `scion echo`, `scion traceroute`, `scion pingpong`, and the soon to be added `scion pathdbdump` (#3707) tools to be part of `scion` eventually.
- Loading branch information
Showing
12 changed files
with
925 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
load("@io_bazel_rules_go//go:def.bzl", "go_library") | ||
|
||
go_library( | ||
name = "go_default_library", | ||
srcs = [ | ||
"config.go", | ||
"showpaths.go", | ||
], | ||
importpath = "github.com/scionproto/scion/go/pkg/showpaths", | ||
visibility = ["//visibility:public"], | ||
deps = [ | ||
"//go/lib/addr:go_default_library", | ||
"//go/lib/common:go_default_library", | ||
"//go/lib/sciond:go_default_library", | ||
"//go/lib/sciond/pathprobe:go_default_library", | ||
"//go/lib/serrors:go_default_library", | ||
"//go/lib/snet:go_default_library", | ||
"//go/lib/snet/addrutil:go_default_library", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Copyright 2020 Anapaya Systems | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package showpaths | ||
|
||
import ( | ||
"net" | ||
) | ||
|
||
// DefaultMaxPaths is the maximum number of paths that are displayed by default. | ||
const DefaultMaxPaths = 10 | ||
|
||
// Config configures the showpath run. | ||
type Config struct { | ||
// Local configures the local IP address to use. If this option is not provided, | ||
// a local IP that can reach SCION hosts is selected with the help of the kernel. | ||
Local net.IP | ||
// SCIOND configures a specific SCION Deamon address. | ||
SCIOND string | ||
// MaxPaths configures the maximum number of displayed paths. If this option is | ||
// not provided, the DefaultMaxPaths is used. | ||
MaxPaths int | ||
// Refresh configures whether sciond is queried with the refresh flag. | ||
Refresh bool | ||
// NoProbe configures whether the path status is probed or not. | ||
NoProbe bool | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
// Copyright 2018 ETH Zurich, Anapaya Systems | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package showpaths | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"net" | ||
"strings" | ||
"time" | ||
|
||
"github.com/scionproto/scion/go/lib/addr" | ||
"github.com/scionproto/scion/go/lib/common" | ||
"github.com/scionproto/scion/go/lib/sciond" | ||
"github.com/scionproto/scion/go/lib/sciond/pathprobe" | ||
"github.com/scionproto/scion/go/lib/serrors" | ||
"github.com/scionproto/scion/go/lib/snet" | ||
"github.com/scionproto/scion/go/lib/snet/addrutil" | ||
) | ||
|
||
// Result contains all the discovered paths. | ||
type Result struct { | ||
Destination addr.IA `json:"destination"` | ||
Paths []Path `json:"paths"` | ||
} | ||
|
||
// Path holds information about the discovered path. | ||
type Path struct { | ||
FullPath snet.Path `json:"-"` | ||
Fingerprint string `json:"fingerprint"` | ||
Hops []Hop `json:"hops"` | ||
NextHop string `json:"next_hop"` | ||
Expiry time.Time `json:"expiry"` | ||
MTU uint16 `json:"mtu"` | ||
Status string `json:"status,omitempty"` | ||
StatusInfo string `json:"status_info,omitempty"` | ||
Local net.IP `json:"local_ip,omitempty"` | ||
} | ||
|
||
// Hop represents an hop on the path. | ||
type Hop struct { | ||
IfID common.IFIDType `json:"ifid"` | ||
IA addr.IA `json:"isd_as"` | ||
} | ||
|
||
// Human writes human readable output to the writer. | ||
func (r Result) Human(w io.Writer, showExpiration bool) { | ||
fmt.Fprintln(w, "Available paths to", r.Destination) | ||
for i, path := range r.Paths { | ||
fmt.Fprintf(w, "[%2d] %s", i, fmt.Sprintf("%s", path.FullPath)) | ||
if showExpiration { | ||
ttl := time.Until(path.Expiry).Truncate(time.Second) | ||
fmt.Fprintf(w, " Expires: %s (%s)", path.Expiry, ttl) | ||
} | ||
if path.Status != "" { | ||
fmt.Fprintf(w, " Status: %s LocalIP: %s", path.Status, path.Local) | ||
} | ||
fmt.Fprintln(w) | ||
} | ||
} | ||
|
||
// JSON writes the showpaths result as a json object to the writer. | ||
func (r Result) JSON(w io.Writer) error { | ||
enc := json.NewEncoder(w) | ||
enc.SetIndent("", " ") | ||
return enc.Encode(r) | ||
} | ||
|
||
// Run lists the paths to the specified ISD-AS to stdout. | ||
func Run(ctx context.Context, dst addr.IA, cfg Config) (*Result, error) { | ||
sdConn, err := sciond.NewService(cfg.SCIOND).Connect(ctx) | ||
if err != nil { | ||
return nil, serrors.WrapStr("error connecting to SCIOND", err) | ||
} | ||
localIA, err := sdConn.LocalIA(ctx) | ||
if err != nil { | ||
return nil, serrors.WrapStr("error determining local ISD-AS", err) | ||
} | ||
|
||
// TODO(lukedirtwalker): Replace this with snet.Router once we have the | ||
// possibility to have the same functionality, i.e. refresh, fetch all paths. | ||
// https://github.com/scionproto/scion/issues/3348 | ||
paths, err := sdConn.Paths(ctx, dst, addr.IA{}, | ||
sciond.PathReqFlags{Refresh: cfg.Refresh, PathCount: uint16(cfg.MaxPaths)}) | ||
if err != nil { | ||
return nil, serrors.WrapStr("failed to retrieve paths from SCIOND", err) | ||
} | ||
|
||
var statuses map[string]pathprobe.Status | ||
var localIP net.IP | ||
if !cfg.NoProbe { | ||
// Resolve local IP in case it is not configured. | ||
if localIP = cfg.Local; localIP == nil { | ||
localIP, err = findDefaultLocalIP(ctx, sdConn) | ||
if err != nil { | ||
return nil, serrors.WrapStr("failed to determine local IP", err) | ||
} | ||
} | ||
statuses, err = pathprobe.Prober{ | ||
DstIA: dst, | ||
LocalIA: localIA, | ||
LocalIP: localIP, | ||
}.GetStatuses(ctx, paths) | ||
if err != nil { | ||
serrors.WrapStr("failed to get status", err) | ||
} | ||
} | ||
|
||
res := &Result{Destination: dst} | ||
for _, path := range paths { | ||
rpath := Path{ | ||
FullPath: path, | ||
Fingerprint: path.Fingerprint().String()[:16], | ||
NextHop: path.OverlayNextHop().String(), | ||
Expiry: path.Expiry(), | ||
MTU: path.MTU(), | ||
Local: localIP, | ||
} | ||
for _, hop := range path.Interfaces() { | ||
rpath.Hops = append(rpath.Hops, Hop{IA: hop.IA(), IfID: hop.ID()}) | ||
} | ||
if status, ok := statuses[pathprobe.PathKey(path)]; ok { | ||
rpath.Status = strings.ToLower(string(status.Status)) | ||
rpath.StatusInfo = status.AdditionalInfo | ||
} | ||
res.Paths = append(res.Paths, rpath) | ||
} | ||
return res, nil | ||
} | ||
|
||
// TODO(matzf): this is a simple, hopefully temporary, workaround to not having | ||
// wildcard addresses in snet. | ||
// Here we just use a seemingly sensible default IP, but in the general case | ||
// the local IP would depend on the next hop of selected path. This approach | ||
// will not work in more complicated setups where e.g. different network | ||
// interface are used to talk to different AS interfaces. | ||
// Once a available, a wildcard address should be used and this should simply | ||
// be removed. | ||
// | ||
// findDefaultLocalIP returns _a_ IP of this host in the local AS. | ||
func findDefaultLocalIP(ctx context.Context, sciondConn sciond.Connector) (net.IP, error) { | ||
hostInLocalAS, err := findAnyHostInLocalAS(ctx, sciondConn) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return addrutil.ResolveLocal(hostInLocalAS) | ||
} | ||
|
||
// findAnyHostInLocalAS returns the IP address of some (infrastructure) host in the local AS. | ||
func findAnyHostInLocalAS(ctx context.Context, sciondConn sciond.Connector) (net.IP, error) { | ||
addr, err := sciond.TopoQuerier{Connector: sciondConn}.OverlayAnycast(ctx, addr.SvcBS) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return addr.IP, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
load("@io_bazel_rules_go//go:def.bzl", "go_library") | ||
load("//:scion.bzl", "scion_go_binary") | ||
|
||
go_library( | ||
name = "go_default_library", | ||
srcs = [ | ||
"completion.go", | ||
"scion.go", | ||
"showpaths.go", | ||
"version.go", | ||
], | ||
importpath = "github.com/scionproto/scion/go/scion", | ||
visibility = ["//visibility:private"], | ||
deps = [ | ||
"//go/lib/addr:go_default_library", | ||
"//go/lib/env:go_default_library", | ||
"//go/lib/log:go_default_library", | ||
"//go/lib/sciond:go_default_library", | ||
"//go/lib/serrors:go_default_library", | ||
"//go/pkg/showpaths:go_default_library", | ||
"@com_github_spf13_cobra//:go_default_library", | ||
], | ||
) | ||
|
||
scion_go_binary( | ||
name = "scion", | ||
embed = [":go_default_library"], | ||
visibility = ["//visibility:public"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// Copyright 2020 Anapaya Systems | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package main | ||
|
||
import ( | ||
"os" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/scionproto/scion/go/lib/serrors" | ||
) | ||
|
||
var completionShell string | ||
|
||
// completionCmd represents the completion command | ||
var completionCmd = &cobra.Command{ | ||
Use: "completion", | ||
Short: "Generates bash completion scripts", | ||
Long: `'completion' outputs the autocomplete configuration for some shells. | ||
For example, you can add autocompletion for your current bash session using: | ||
. <( scion completion ) | ||
To permanently add bash autocompletion, run: | ||
scion completion > /etc/bash_completion.d/scion | ||
`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
switch completionShell { | ||
case "bash": | ||
return rootCmd.GenBashCompletion(os.Stdout) | ||
case "zsh": | ||
return rootCmd.GenZshCompletion(os.Stdout) | ||
case "fish": | ||
return rootCmd.GenFishCompletion(os.Stdout, true) | ||
default: | ||
return serrors.New("unknown shell", "input", completionShell) | ||
} | ||
}, | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(completionCmd) | ||
completionCmd.Flags().StringVar(&completionShell, "shell", "bash", | ||
"Shell type (bash|zsh|fish)") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// Copyright 2020 Anapaya Systems | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
func main() { | ||
if err := rootCmd.Execute(); err != nil { | ||
fmt.Fprintf(os.Stderr, "Error: %s\n", err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
var rootCmd = &cobra.Command{ | ||
Use: "scion", | ||
Short: "A clean-slate internet architecture", | ||
Args: cobra.NoArgs, | ||
// Silence the errors, since we print them in main. Otherwise, cobra | ||
// will print any non-nil errors returned by a RunE function. | ||
// See https://github.com/spf13/cobra/issues/340. | ||
// Commands should turn off the usage help message, if they deem the arguments | ||
// to be reasonable well-formed. This avoids outputing help message on errors | ||
// that are not caused by malformed input. | ||
// See https://github.com/spf13/cobra/issues/340#issuecomment-374617413. | ||
SilenceErrors: true, | ||
} |
Oops, something went wrong.