-
Notifications
You must be signed in to change notification settings - Fork 832
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 resource generic load, unload, status to CLI #4660
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package cli | ||
|
||
import ( | ||
"k8s.io/utils/env" | ||
|
||
"github.com/seldonio/seldon-core/operator/v2/pkg/cli" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func createLoad() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "load", | ||
Short: "load resources", | ||
Long: `load resources`, | ||
Args: cobra.MinimumNArgs(0), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
flags := cmd.Flags() | ||
|
||
schedulerHostIsSet := flags.Changed(flagSchedulerHost) | ||
schedulerHost, err := flags.GetString(flagSchedulerHost) | ||
if err != nil { | ||
return err | ||
} | ||
authority, err := flags.GetString(flagAuthority) | ||
if err != nil { | ||
return err | ||
} | ||
filename, err := flags.GetString(flagFile) | ||
if err != nil { | ||
return err | ||
} | ||
showRequest, err := flags.GetBool(flagShowRequest) | ||
if err != nil { | ||
return err | ||
} | ||
showResponse, err := flags.GetBool(flagShowResponse) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
schedulerClient, err := cli.NewSchedulerClient(schedulerHost, schedulerHostIsSet, authority) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var dataFile []byte | ||
if filename != "" { | ||
dataFile = loadFile(filename) | ||
} | ||
err = schedulerClient.Load(dataFile, showRequest, showResponse) | ||
return err | ||
}, | ||
} | ||
|
||
flags := cmd.Flags() | ||
flags.BoolP(flagShowRequest, "r", false, "show request") | ||
flags.BoolP(flagShowResponse, "o", false, "show response") | ||
flags.String(flagSchedulerHost, env.GetString(envScheduler, defaultSchedulerHost), helpSchedulerHost) | ||
flags.String(flagAuthority, "", helpAuthority) | ||
flags.StringP(flagFile, "f", "", "model manifest file (YAML)") | ||
|
||
return cmd | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package cli | ||
|
||
import ( | ||
"k8s.io/utils/env" | ||
|
||
"github.com/seldonio/seldon-core/operator/v2/pkg/cli" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func createStatus() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "status <pipelineName>", | ||
Short: "status of a pipeline", | ||
Long: `status of a pipeline`, | ||
Args: cobra.ExactArgs(0), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
flags := cmd.Flags() | ||
|
||
schedulerHostIsSet := flags.Changed(flagSchedulerHost) | ||
schedulerHost, err := flags.GetString(flagSchedulerHost) | ||
if err != nil { | ||
return err | ||
} | ||
authority, err := flags.GetString(flagAuthority) | ||
if err != nil { | ||
return err | ||
} | ||
showRequest, err := flags.GetBool(flagShowRequest) | ||
if err != nil { | ||
return err | ||
} | ||
showResponse, err := flags.GetBool(flagShowResponse) | ||
if err != nil { | ||
return err | ||
} | ||
waitCondition, err := flags.GetBool(flagWaitCondition) | ||
if err != nil { | ||
return err | ||
} | ||
filename, err := flags.GetString(flagFile) | ||
if err != nil { | ||
return err | ||
} | ||
var dataFile []byte | ||
if filename != "" { | ||
dataFile = loadFile(filename) | ||
} | ||
schedulerClient, err := cli.NewSchedulerClient(schedulerHost, schedulerHostIsSet, authority) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = schedulerClient.Status(dataFile, showRequest, showResponse, waitCondition) | ||
return err | ||
}, | ||
} | ||
|
||
flags := cmd.Flags() | ||
flags.BoolP(flagShowRequest, "r", false, "show request") | ||
flags.BoolP(flagShowResponse, "o", false, "show response") | ||
flags.String(flagSchedulerHost, env.GetString(envScheduler, defaultSchedulerHost), helpSchedulerHost) | ||
flags.String(flagAuthority, "", helpAuthority) | ||
flags.BoolP(flagWaitCondition, "w", false, "wait for resources to be ready") | ||
flags.StringP(flagFile, "f", "", "model manifest file (YAML)") | ||
|
||
return cmd | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package cli | ||
|
||
import ( | ||
"k8s.io/utils/env" | ||
|
||
"github.com/seldonio/seldon-core/operator/v2/pkg/cli" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func createUnload() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "unload resources", | ||
Short: "unload resources", | ||
Long: `unload resources`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
flags := cmd.Flags() | ||
|
||
schedulerHostIsSet := flags.Changed(flagSchedulerHost) | ||
schedulerHost, err := flags.GetString(flagSchedulerHost) | ||
if err != nil { | ||
return err | ||
} | ||
authority, err := flags.GetString(flagAuthority) | ||
if err != nil { | ||
return err | ||
} | ||
showRequest, err := flags.GetBool(flagShowRequest) | ||
if err != nil { | ||
return err | ||
} | ||
showResponse, err := flags.GetBool(flagShowResponse) | ||
if err != nil { | ||
return err | ||
} | ||
filename, err := flags.GetString(flagFile) | ||
if err != nil { | ||
return err | ||
} | ||
var dataFile []byte | ||
if filename != "" { | ||
dataFile = loadFile(filename) | ||
} | ||
|
||
schedulerClient, err := cli.NewSchedulerClient(schedulerHost, schedulerHostIsSet, authority) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = schedulerClient.Unload(dataFile, showRequest, showResponse) | ||
sakoush marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return err | ||
}, | ||
} | ||
|
||
flags := cmd.Flags() | ||
flags.BoolP(flagShowRequest, "r", false, "show request") | ||
flags.BoolP(flagShowResponse, "o", false, "show response") | ||
flags.String(flagSchedulerHost, env.GetString(envScheduler, defaultSchedulerHost), helpSchedulerHost) | ||
flags.String(flagAuthority, "", helpAuthority) | ||
flags.StringP(flagFile, "f", "", "model manifest file (YAML)") | ||
|
||
return cmd | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,109 @@ | ||||||
package cli | ||||||
|
||||||
import ( | ||||||
"bytes" | ||||||
"errors" | ||||||
"io" | ||||||
"os" | ||||||
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||||||
yaml2 "k8s.io/apimachinery/pkg/util/yaml" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does it have to be |
||||||
) | ||||||
|
||||||
func createDecoder(data []byte) *yaml2.YAMLOrJSONDecoder { | ||||||
var reader io.Reader | ||||||
if len(data) > 0 { | ||||||
reader = bytes.NewReader(data) | ||||||
} else { | ||||||
reader = io.Reader(os.Stdin) | ||||||
} | ||||||
dec := yaml2.NewYAMLOrJSONDecoder(reader, 10) | ||||||
return dec | ||||||
} | ||||||
|
||||||
func getNextResource(dec *yaml2.YAMLOrJSONDecoder) (string, string, []byte, bool, error) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. too many returns and it is hard to know what we are getting back. Consider adding at least description. |
||||||
unstructuredObject := &unstructured.Unstructured{} | ||||||
if err := dec.Decode(unstructuredObject); err != nil { | ||||||
if errors.Is(err, io.EOF) { | ||||||
return "", "", nil, false, nil | ||||||
} | ||||||
return "", "", nil, false, err | ||||||
} | ||||||
// Get the resource kind nad original bytes | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
gvk := unstructuredObject.GroupVersionKind() | ||||||
data, err := unstructuredObject.MarshalJSON() | ||||||
if err != nil { | ||||||
return "", "", nil, false, err | ||||||
} | ||||||
return gvk.Kind, unstructuredObject.GetName(), data, true, nil | ||||||
} | ||||||
|
||||||
func (sc *SchedulerClient) Load(data []byte, showRequest bool, showResponse bool) error { | ||||||
dec := createDecoder(data) | ||||||
for { | ||||||
kind, _, data, keepGoing, err := getNextResource(dec) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we are also not checking |
||||||
if !keepGoing { | ||||||
return err | ||||||
} | ||||||
switch kind { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we add a warning if it is not part of these 3 resources? should we stop? |
||||||
case "Model": | ||||||
err = sc.LoadModel(data, showRequest, showResponse) | ||||||
case "Pipeline": | ||||||
err = sc.LoadPipeline(data, showRequest, showResponse) | ||||||
case "Experiment": | ||||||
err = sc.StartExperiment(data, showRequest, showResponse) | ||||||
} | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
func (sc *SchedulerClient) Unload(data []byte, showRequest bool, showResponse bool) error { | ||||||
dec := createDecoder(data) | ||||||
for { | ||||||
kind, _, data, keepGoing, err := getNextResource(dec) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||||||
if !keepGoing { | ||||||
return err | ||||||
} | ||||||
switch kind { | ||||||
case "Model": | ||||||
err = sc.UnloadModel("", data, showRequest, showResponse) | ||||||
case "Pipeline": | ||||||
err = sc.UnloadPipeline("", data, showRequest, showResponse) | ||||||
case "Experiment": | ||||||
err = sc.StopExperiment("", data, showRequest, showResponse) | ||||||
} | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
func (sc *SchedulerClient) Status(data []byte, showRequest bool, showResponse bool, wait bool) error { | ||||||
dec := createDecoder(data) | ||||||
for { | ||||||
kind, name, _, keepGoing, err := getNextResource(dec) | ||||||
if !keepGoing { | ||||||
return err | ||||||
} | ||||||
waitCondition := "" | ||||||
switch kind { | ||||||
case "Model": | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. consider extracting the values of the switch into enum as it is used in 3 different places already. |
||||||
if wait { | ||||||
waitCondition = "ModelAvailable" | ||||||
} | ||||||
err = sc.ModelStatus(name, showRequest, showResponse, waitCondition) | ||||||
case "Pipeline": | ||||||
if wait { | ||||||
waitCondition = "PipelineReady" | ||||||
} | ||||||
err = sc.PipelineStatus(name, showRequest, showResponse, waitCondition) | ||||||
case "Experiment": | ||||||
err = sc.ExperimentStatus(name, showRequest, showResponse, wait) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no wait on Experiments? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the bool is passed directly to function - experiments don't take a string waitCondition |
||||||
} | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just to keep consistency with previous usage (i.e.
seldon model load
andseldon pipeline load
), an alternative is perhaps to useseldon all load
? I am personally not entirely sure which approach is better so happy to leave it as is.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems a bit verbose - maybe will leave for now