Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CLI to kuberay #135

Merged
merged 1 commit into from
Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/test-job.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
build:
env:
working-directory: ./ray-operator
cli-working-directory: ./cli
name: Build
runs-on: ubuntu-latest
steps:
Expand All @@ -74,7 +75,7 @@ jobs:
run: go get golang.org/x/tools/cmd/goimports

- name: Run goimports
run: test -z "$(set -o pipefail && $(go env GOPATH)/bin/goimports -l apiserver/ ray-operator/ | tee goimports.out)" || { cat goimports.out && exit 1; }
run: test -z "$(set -o pipefail && $(go env GOPATH)/bin/goimports -l apiserver/ ray-operator/ cli/ | tee goimports.out)" || { cat goimports.out && exit 1; }

- name: Open this to see how to fix goimports if it fails
run: echo Run goimports -w .
Expand Down Expand Up @@ -107,3 +108,7 @@ jobs:
- name: Build Docker Image
run: make docker-image
working-directory: ${{env.working-directory}}

- name: Build CLI
run: go build -o kuberay -a main.go
working-directory: ${{env.cli-working-directory}}
80 changes: 80 additions & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# KubeRay CLI

[![Build Status](https://github.com/ray-project/kuberay/workflows/Go-build-and-test/badge.svg)](https://github.com/ray-project/kuberay/actions)
[![Go Report Card](https://goreportcard.com/badge/github.com/ray-project/kuberay)](https://goreportcard.com/report/github.com/ray-project/kuberay)

# Overview
KubeRay CLI provides the ability to manage kuberay resources (ray clusters, compute templates etc) through command line interface.

# Implementation
- Kuberay CLI uses [Cobra framework](https://github.com/spf13/cobra) for the CLI application.
- Kuberay CLI depends on kuberay apiserver to manage these resources by sending grpc requests to the kuberay apiserver.

# Installation
TBD

# Prerequisites
- Kuberay apiserver needs to be running and accessible.

# Build
`cd kuberay/cli`
`go build -o kuberay -a main.go`

# Use
## Connect to kuberay apiserver
- Default kuberay apiserver endpoint: `127.0.0.1:8887`.
- If kuberay apiserver is not run locally, this must be set in order to manage ray clusters and ray compute templates.
### Read current kuberay apiserver endpoint
`./kuberay config get endpoint`
### Reset kuberay apiserver endpoint to default (`127.0.0.1:8887`)
`./kuberay config reset endpoint`
### Set kuberay apiserver endpoint
`./kuberay config set endpoint <kuberay apiserver endpoint>`
## Manage Ray Clusters
### Create a Ray Cluster
````
Usage:
kuberay cluster create [flags]
Flags:
--environment string environment of the cluster (valid values: DEV, TESTING, STAGING, PRODUCTION) (default "DEV")
--head-compute-tempalte string compuate template name for ray head
--head-image string ray head image
--head-service-type string ray head service type (ClusterIP, NodePort, LoadBalancer) (default "ClusterIP")
--name string name of the cluster
--namespace string kubernetes namespace where the cluster will be (default "ray-system")
--user string SSO username of ray cluster creator
--version string version of the ray cluster (default "1.9.0")
--worker-compute-template string compute template name of worker in the first worker group
--worker-group-name string first worker group name
--worker-image string image of worker in the first worker group
--worker-replicas uint32 pod replicas of workers in the first worker group (default 1)
````
- Limitation: Currently only one worker compute template is supported during creation.
### Get a Ray Cluster
`./kuberay cluster get <cluster name>`
### List Ray Clusters
`./kuberay cluster list`
### Delete a Ray Cluster
`./kuberay cluster delete <cluster name>`
## Manage Ray Compute Template
### Create a Compute Template
````
Usage:
kuberay template compute create [flags]
Flags:
--cpu uint32 ray pod CPU (default 1)
--gpu uint32 ray head GPU
--gpu-accelerator string GPU Accelerator type
--memory uint32 ray pod memory in GB (default 1)
--name string name of the compute template
````
### Get a Ray Compute Template
`./kuberay template compute get <compute template name>`
### List Ray Compute Templates
`./kuberay template compute list`
### Delete a Ray Compute Template
`./kuberay template compute delete <compute template name>`

154 changes: 154 additions & 0 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package cmd

import (
"fmt"
"os"
"strings"
"time"

"github.com/ray-project/kuberay/cli/pkg/cmd/cluster"
"github.com/ray-project/kuberay/cli/pkg/cmd/config"
"github.com/ray-project/kuberay/cli/pkg/cmd/info"
"github.com/ray-project/kuberay/cli/pkg/cmd/template"
"github.com/ray-project/kuberay/cli/pkg/cmd/version"
"github.com/ray-project/kuberay/cli/pkg/cmdutil"
"github.com/spf13/cobra"

"github.com/fatih/color"
"github.com/kris-nova/logger"
lol "github.com/kris-nova/lolgopher"
"github.com/spf13/viper"
)

var cfgFile string

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "kuberay",
Short: "kuberay offers life cycle management of ray clusters",
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
cobra.CheckErr(rootCmd.Execute())
}

func init() {
loggerLevel := rootCmd.PersistentFlags().IntP("log-level", "l", 3, "set log level, use 0 to silence, 4 for debugging and 5 for debugging with AWS debug logging")
colorValue := rootCmd.PersistentFlags().StringP("color", "C", "true", "toggle colorized logs (valid options: true, false, fabulous)")
cobra.OnInitialize(initConfig, func() {
initLogger(*loggerLevel, *colorValue)
})

// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.

rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.kuberay.yaml)")

// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

rootCmd.PersistentFlags().BoolP("help", "h", false, "help for this command")
rootCmd.AddCommand(info.NewCmdInfo())
rootCmd.AddCommand(version.NewCmdVersion())
rootCmd.AddCommand(cluster.NewCmdCluster())
rootCmd.AddCommand(template.NewCmdTemplate())
rootCmd.AddCommand(config.NewCmdConfig())
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := os.UserHomeDir()
cobra.CheckErr(err)

// Search config in home directory with name ".cli" (without extension).
viper.AddConfigPath(home)
viper.SetConfigType("yaml")
viper.SetConfigName(".kuberay")

viper.SetDefault("endpoint", fmt.Sprintf("%s:%s", cmdutil.DefaultRpcAddress, cmdutil.DefaultRpcPort))
// Do not write to file system if it already exists
viper.SafeWriteConfig()
}

viper.AutomaticEnv() // read in environment variables that match

viper.ReadInConfig()
}

func initLogger(level int, colorValue string) {
logger.Layout = "2021-01-02 15:04:05"

var bitwiseLevel int
switch level {
case 4:
bitwiseLevel = logger.LogDeprecated | logger.LogAlways | logger.LogSuccess | logger.LogCritical | logger.LogWarning | logger.LogInfo | logger.LogDebug
case 3:
bitwiseLevel = logger.LogDeprecated | logger.LogAlways | logger.LogSuccess | logger.LogCritical | logger.LogWarning | logger.LogInfo
case 2:
bitwiseLevel = logger.LogDeprecated | logger.LogAlways | logger.LogSuccess | logger.LogCritical | logger.LogWarning
case 1:
bitwiseLevel = logger.LogDeprecated | logger.LogAlways | logger.LogSuccess | logger.LogCritical
case 0:
bitwiseLevel = logger.LogDeprecated | logger.LogAlways | logger.LogSuccess
default:
bitwiseLevel = logger.LogDeprecated | logger.LogEverything
}
logger.BitwiseLevel = bitwiseLevel

switch colorValue {
case "fabulous":
logger.Writer = lol.NewLolWriter()
case "true":
logger.Writer = color.Output
}

logger.Line = func(prefix, format string, a ...interface{}) string {
if !strings.Contains(format, "\n") {
format = fmt.Sprintf("%s%s", format, "\n")
}
now := time.Now()
fNow := now.Format(logger.Layout)
var colorize func(format string, a ...interface{}) string
var icon string
switch prefix {
case logger.PreAlways:
icon = "✿"
colorize = color.GreenString
case logger.PreCritical:
icon = "✖"
colorize = color.RedString
case logger.PreInfo:
icon = "ℹ"
colorize = color.CyanString
case logger.PreDebug:
icon = "▶"
colorize = color.GreenString
case logger.PreSuccess:
icon = "✔"
colorize = color.CyanString
case logger.PreWarning:
icon = "!"
colorize = color.GreenString
default:
icon = "ℹ"
colorize = color.CyanString
}

out := fmt.Sprintf(format, a...)
out = fmt.Sprintf("%s [%s] %s", fNow, icon, out)
if colorValue == "true" {
out = colorize(out)
}

return out
}
}
42 changes: 42 additions & 0 deletions cli/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module github.com/ray-project/kuberay/cli

go 1.17

require (
github.com/fatih/color v1.13.0
github.com/kris-nova/logger v0.2.2
github.com/kris-nova/lolgopher v0.0.0-20210112022122-73f0047e8b65
github.com/olekukonko/tablewriter v0.0.5
github.com/ray-project/kuberay/proto v0.0.0-20220119062608-4054f1bf1765
github.com/spf13/cobra v1.3.0
github.com/spf13/viper v1.10.1
google.golang.org/grpc v1.43.0
)

require (
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
Loading