Skip to content

Commit

Permalink
Add porchctl command
Browse files Browse the repository at this point in the history
  • Loading branch information
johnbelamaric committed Dec 19, 2023
1 parent 5b7eab4 commit d3f576a
Show file tree
Hide file tree
Showing 34 changed files with 4,629 additions and 4 deletions.
88 changes: 88 additions & 0 deletions cmd/porchctl/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2023 The kpt and Nephio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES 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 (
"context"
"fmt"
"os"

"github.com/nephio-project/porch/cmd/porchctl/run"
"github.com/nephio-project/porch/internal/kpt/errors"
"github.com/nephio-project/porch/internal/kpt/errors/resolver"
"github.com/nephio-project/porch/internal/kpt/util/cmdutil"
"github.com/spf13/cobra"
_ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/component-base/cli"
"k8s.io/klog/v2"
k8scmdutil "k8s.io/kubectl/pkg/cmd/util"
)

func main() {
// Handle all setup in the runMain function so os.Exit doesn't interfere
// with defer.
os.Exit(runMain())
}

// runMain does the initial setup in order to run kpt. The return value from
// this function will be the exit code when kpt terminates.
func runMain() int {
var err error

ctx := context.Background()

// Enable commandline flags for klog.
// logging will help in collecting debugging information from users
klog.InitFlags(nil)

cmd := run.GetMain(ctx)

err = cli.RunNoErrOutput(cmd)
if err != nil {
return handleErr(cmd, err)
}
return 0
}

// handleErr takes care of printing an error message for a given error.
func handleErr(cmd *cobra.Command, err error) int {
// First attempt to see if we can resolve the error into a specific
// error message.
if re, resolved := resolver.ResolveError(err); resolved {
if re.Message != "" {
fmt.Fprintf(cmd.ErrOrStderr(), "%s \n", re.Message)
}
return re.ExitCode
}

// Then try to see if it is of type *errors.Error
var kptErr *errors.Error
if errors.As(err, &kptErr) {
unwrapped, ok := errors.UnwrapErrors(kptErr)
if ok && !cmdutil.PrintErrorStacktrace() {
fmt.Fprintf(cmd.ErrOrStderr(), "Error: %s \n", unwrapped.Error())
return 1
}
fmt.Fprintf(cmd.ErrOrStderr(), "%s \n", kptErr.Error())
return 1
}

// Finally just let the error handler for kubectl handle it. This handles
// printing of several error types used in kubectl
// TODO: See if we can handle this in kpt and get a uniform experience
// across all of kpt.
k8scmdutil.CheckErr(err)
return 1
}
210 changes: 210 additions & 0 deletions cmd/porchctl/run/run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
// Copyright 2023 The kpt and Nephio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package run

import (
"bytes"
"context"
"flag"
"fmt"
"os"
"os/exec"
"strconv"
"strings"

"github.com/nephio-project/porch/internal/kpt/util/cmdutil"
"github.com/nephio-project/porch/pkg/cli/commands"
"github.com/nephio-project/porch/pkg/kpt/printer"
"github.com/spf13/cobra"
)

var pgr []string

const description = `
porchctl interacts with a Kubernetes API server with the Porch
server installed as an aggregated API server. It allows you to
manage Porch repository registrations and the packages within
those repositories.
`

func GetMain(ctx context.Context) *cobra.Command {
cmd := &cobra.Command{
Use: "porchctl",
Short: "Manage porch repositories and packages",
Long: description,
SilenceUsage: true,
// We handle all errors in main after return from cobra so we can
// adjust the error message coming from libraries
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {
h, err := cmd.Flags().GetBool("help")
if err != nil {
return err
}
if h {
return cmd.Help()
}
return cmd.Usage()
},
}

cmd.PersistentFlags().AddGoFlagSet(flag.CommandLine)

cmd.PersistentFlags().BoolVar(&printer.TruncateOutput, "truncate-output", true,
"Enable the truncation for output")
// wire the global printer
pr := printer.New(cmd.OutOrStdout(), cmd.ErrOrStderr())

// create context with associated printer
ctx = printer.WithContext(ctx, pr)

// find the pager if one exists
func() {
if val, found := os.LookupEnv("PORCHCTL_NO_PAGER_HELP"); !found || val != "1" {
// use a pager for printing tutorials
e, found := os.LookupEnv("PAGER")
var err error
if found {
pgr = []string{e}
return
}
e, err = exec.LookPath("pager")
if err == nil {
pgr = []string{e}
return
}
e, err = exec.LookPath("less")
if err == nil {
pgr = []string{e, "-R"}
return
}
}
}()

// help and documentation
cmd.InitDefaultHelpCmd()
cmd.AddCommand(commands.GetCommands(ctx, "porchctl", version)...)

// enable stack traces
cmd.PersistentFlags().BoolVar(&cmdutil.StackOnError, "stack-trace", false,
"Print a stack-trace on failure")

if _, err := exec.LookPath("git"); err != nil {
fmt.Fprintf(os.Stderr, "porchctl requires that `git` is installed and on the PATH")
os.Exit(1)
}

replace(cmd)

cmd.AddCommand(versionCmd)
hideFlags(cmd)
return cmd
}

func replace(c *cobra.Command) {
for i := range c.Commands() {
replace(c.Commands()[i])
}
c.SetHelpFunc(newHelp(pgr, c))
}

func newHelp(e []string, c *cobra.Command) func(command *cobra.Command, strings []string) {
if len(pgr) == 0 {
return c.HelpFunc()
}

fn := c.HelpFunc()
return func(command *cobra.Command, args []string) {
stty := exec.Command("stty", "size")
stty.Stdin = os.Stdin
out, err := stty.Output()
if err == nil {
terminalHeight, err := strconv.Atoi(strings.Split(string(out), " ")[0])
helpHeight := strings.Count(command.Long, "\n") +
strings.Count(command.UsageString(), "\n")
if err == nil && terminalHeight > helpHeight {
// don't use a pager if the help is shorter than the console
fn(command, args)
return
}
}

b := &bytes.Buffer{}
pager := exec.Command(e[0])
if len(e) > 1 {
pager.Args = append(pager.Args, e[1:]...)
}
pager.Stdin = b
pager.Stdout = c.OutOrStdout()
c.SetOut(b)
fn(command, args)
if err := pager.Run(); err != nil {
fmt.Fprintf(c.ErrOrStderr(), "%v", err)
os.Exit(1)
}
}
}

var version = "unknown"

var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number of porchctl",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("%s\n", version)
},
}

// hideFlags hides any cobra flags that are unlikely to be used by
// customers.
func hideFlags(cmd *cobra.Command) {
flags := []string{
// Flags related to logging
"add_dir_header",
"alsologtostderr",
"log_backtrace_at",
"log_dir",
"log_file",
"log_file_max_size",
"logtostderr",
"one_output",
"skip_headers",
"skip_log_headers",
"stack-trace",
"stderrthreshold",
"vmodule",

// Flags related to apiserver
"as",
"as-group",
"cache-dir",
"certificate-authority",
"client-certificate",
"client-key",
"insecure-skip-tls-verify",
"match-server-version",
"password",
"token",
"username",
}
for _, f := range flags {
_ = cmd.PersistentFlags().MarkHidden(f)
}

// We need to recurse into subcommands otherwise flags aren't hidden on leaf commands
for _, child := range cmd.Commands() {
hideFlags(child)
}
}
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ require (
google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.31.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
gotest.tools v2.2.0+incompatible
k8s.io/api v0.26.9
k8s.io/apimachinery v0.26.9
Expand Down Expand Up @@ -101,6 +102,7 @@ require (
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/fvbommel/sortorder v1.0.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
Expand All @@ -110,7 +112,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/cel-go v0.12.7 // indirect
github.com/google/cel-go v0.13.0 // indirect
github.com/google/gnostic v0.6.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/s2a-go v0.1.4 // indirect
Expand Down Expand Up @@ -184,7 +186,6 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.26.9 // indirect
k8s.io/kms v0.26.9 // indirect
k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE=
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
Expand Down Expand Up @@ -328,8 +330,8 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.12.7 h1:jM6p55R0MKBg79hZjn1zs2OlrywZ1Vk00rxVvad1/O0=
github.com/google/cel-go v0.12.7/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=
github.com/google/cel-go v0.13.0 h1:z+8OBOcmh7IeKyqwT/6IlnMvy621fYUqnTVPEdegGlU=
github.com/google/cel-go v0.13.0/go.mod h1:K2hpQgEjDp18J76a2DKFRlPBPpgRZgi6EbnpDgIhJ8s=
github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0=
github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E=
Expand Down
Loading

0 comments on commit d3f576a

Please sign in to comment.