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

[ADDED] system model command #8284

Merged
merged 43 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
e1d4211
"system model list" command is added
zakisk Jul 19, 2023
25389b6
error and links are added for list command
zakisk Jul 20, 2023
8e516a2
"system model list" command is added
zakisk Jul 19, 2023
a989e4e
error and links are added for list command
zakisk Jul 20, 2023
8c78182
"system model view" command is added
zakisk Jul 22, 2023
59821db
Merge branch 'meshery:master' into add_system_model_command
zakisk Jul 24, 2023
62c8f58
minor changes
zakisk Jul 24, 2023
c707a22
Merge remote-tracking branch 'upstream/master' into add_system_model_…
zakisk Jul 26, 2023
d149bff
[TEST] Add Unit Test for `mesheryctl system model list` command
zakisk Jul 26, 2023
6377c27
Merge branch 'meshery:master' into add_system_model_command
zakisk Jul 26, 2023
cea9125
[TEST] Add Unit Test for `mesheryctl system model view` command
zakisk Jul 26, 2023
f19f89a
Merge branch 'meshery:master' into add_system_model_command
zakisk Jul 26, 2023
dabd8b7
Revert "[TEST] Add Unit Test for `mesheryctl system model view` command"
zakisk Jul 26, 2023
88d8809
minor rename
zakisk Jul 26, 2023
6fd6987
Merge remote-tracking branch 'upstream/master' into add_system_model_…
zakisk Jul 28, 2023
c6b2dea
Merge branch 'meshery:master' into add_system_model_command
zakisk Jul 28, 2023
468fa1c
Merge branch 'add_system_model_command' of https://github.com/zakisk/…
zakisk Jul 28, 2023
1bd4baf
Merge branch 'meshery:master' into add_system_model_command
zakisk Jul 29, 2023
183e62a
Merged latest changes from origin
zakisk Jul 29, 2023
ff6e87c
used utils.MakeRequest
zakisk Jul 29, 2023
fbe5ea2
renamed variable cfg and deleted unused comments
zakisk Jul 29, 2023
45f0132
added error.go file
zakisk Jul 30, 2023
5f44d18
Merge branch 'meshery:master' into add_system_model_command
zakisk Jul 30, 2023
4c7b439
changes suggested by @suhail34
zakisk Jul 31, 2023
20ac6ef
deleted duplicate errors
zakisk Jul 31, 2023
f44ef28
Merge branch 'meshery:master' into add_system_model_command
zakisk Jul 31, 2023
0e5787a
Merge branch 'meshery:master' into add_system_model_command
zakisk Aug 1, 2023
a3e9cc8
Merge branch 'meshery:master' into add_system_model_command
zakisk Aug 1, 2023
d784e1d
Merge branch 'meshery:master' into add_system_model_command
zakisk Aug 5, 2023
d0261a3
Merge branch 'meshery:master' into add_system_model_command
zakisk Aug 12, 2023
a42df4d
formatted output of model list command in tabular form
zakisk Aug 12, 2023
e04c3e8
Merge remote-tracking branch 'upstream/master' into add_system_model_…
zakisk Aug 13, 2023
60427ca
minor changes
zakisk Aug 13, 2023
0ede87c
Merge remote-tracking branch 'upstream/master' into add_system_model_…
zakisk Aug 15, 2023
f1f2c41
minor error messages changes
zakisk Aug 15, 2023
67054c7
Merge remote-tracking branch 'upstream/master' into add_system_model_…
zakisk Aug 26, 2023
eda44e5
Merge remote-tracking branch 'upstream/master' into add_system_model_…
zakisk Sep 1, 2023
aad1845
"system model list" command is added
zakisk Jul 19, 2023
8db6406
Merge remote-tracking branch 'upstream/master' into add_system_model_…
zakisk Sep 25, 2023
100c595
added server running status check before executing system model subco…
zakisk Sep 25, 2023
5e487f2
Merge remote-tracking branch 'upstream/master' into add_system_model_…
zakisk Oct 30, 2023
436f2e8
Merge remote-tracking branch 'upstream/master' into add_system_model_…
zakisk Nov 22, 2023
b3afb62
moved `model` command behind `exp` command
zakisk Nov 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion mesheryctl/internal/cli/root/filter/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func TestFilterCmd(t *testing.T) {
},
{
Name: "filter viewcmd with Invalid ID",
Args: []string{"view","957fbc9b-a655-4892-823d-375102a9587c"},
Args: []string{"view", "957fbc9b-a655-4892-823d-375102a9587c"},
Token: filepath.Join(fixturesDir, "token.golden"),
ExpectedResponse: "filter.invalidID.view.output.golden",
Fixture: "filter.invalidID.view.response.golden",
Expand Down

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions mesheryctl/internal/cli/root/system/fixtures/token.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"meshery-provider":"Meshery","token":"eyJhY2Nlc3NfdG9rZW4iOiJleUpoYkdjaU9pSlNVekkxTmlJc0ltdHBaQ0k2SW5CMVlteHBZenBsT0dWbU5ERmpNeTFpWldWbUxUUmlZakV0T0dVNE1DMHpOakExTVRZeU4yTTJNakVpTENKMGVYQWlPaUpLVjFRaWZRLmV5SmhkV1FpT2x0ZExDSmpiR2xsYm5SZmFXUWlPaUp0WlhOb1pYSjVMV05zYjNWa0lpd2laWGh3SWpveE5qSXlPREk1TlRRMExDSmxlSFFpT250OUxDSnBZWFFpT2pFMk1qSTRNalU1TkRNc0ltbHpjeUk2SW1oMGRIQnpPaTh2YldWemFHVnllUzVzWVhsbGNqVXVhVzh2YUhsa2NtRXZJaXdpYW5ScElqb2lPRGMxT0RGbVpXSXROMlZpTnkwMFlqSTFMV0l3TURndE9XWTJaVEE0WXpabFkyVTJJaXdpYm1KbUlqb3hOakl5T0RJMU9UUXpMQ0p6WTNBaU9sc2liM0JsYm1sa0lpd2liMlptYkdsdVpTSmRMQ0p6ZFdJaU9pSmpSMncxWkZoT2IyTXliSFZhTWtaNVlWaHNhRHBhTW13d1lVaFdhU0o5Lk90aDJwYkJFNmFBcnBfUFVwR3E3b2ZsaEVWYmdsdTAtamdXNG44eWxHeVVTandOc0k4SmdoallIVGU5YjlUSzhWQUhoNVRyT0YwV1VRb0h4QVJGUmN6OHl2ZEdpbm1HcUZEZTd6RVpoSjZHZmNlZFl6bmpCc3FvVWthMTNXYzhvM0J2bGR2T2gtTjFGNzdHM3ZLenI0UEJaM2pXRHVEeWpjSUJnOTJVUzd0Nlg5Ymd6YklrT3lOOVhpWGVVNXQtbEJIamt2cklRazhqdWRKaTliOHVGaVBuMmdIMDVJbnhUdFJtSlFJdUhvSzV2WmxFQW0xN1J6ZER4WVI0cndqeTBqanFWdXdvWnBjbUJQM1dUNjdIVHhkYmo5N3hZM2IzNHh5ZFkxeVFVS09XR1NOckZVeXhMbW9QMmJUM24tQ0dVczJ1SWhnZExXNlZlNVQ1LV9tSGY0Z212X0NGWlFNelRsbjRFVmw2bTUxdjFxNXJzQmdfWmFuVmtXdGNHWF9ZSGs3WHpKdndXRDhvSmt5NzBleGUwYXJ3cmg2bjJkLU9jMi1Jc1F2OTBFM1hYeHBJcWxrckNfU3NiM1NpOU1jM1ptal9HY2JtOHVHbUZEejhaZEYxUEdpeDdKTjM3TzJyQnpaVldRaHFrZTV6MW42VUVITXJGSGJBNXBKVkxzUmE0ZUNBaFdwODVlZVV3ZjlUMnByc3FzNHBaMkh0eVpSMlBTdGFLZVFFai1SUXdvRHpDTEN4Zm85RnBvbEN6WmN3ZzRvLXhrb0Q0aS1MczIzODd0dm5xSTVESl8xaUlMX1hNTHByZXJtcDdxeGV2NEVDOW9abzdWenZmTDd4cDZTcnhIaldZQVpuZS12eURjQlhNZUlSMVVoeVdVZDQtaWJfZmxzdFVEME5XVV9ZIiwidG9rZW5fdHlwZSI6ImJlYXJlciIsInJlZnJlc2hfdG9rZW4iOiJXS3pZWW5BQkVJQkduekNfaWR2VW1IZUtsZlgzLWxjWm12TzBxY2ZCNlRzLm5kNXhXUFFIeWVTcTY0OUV2dy1tX2t3WDdqYWF1RDZiSExXTW9fQVhxZVUiLCJleHBpcnkiOiIyMDIxLTA2LTA0VDE3OjU5OjAzLjg0ODAyODAwOVoifQ"}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we exposing a secret publicly like this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet answered...

alphaX86 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"page":1,"page_size":1,"total_count":1,"models":[{"name":"spire","version":"0.12.2","displayName":"SPIRE","category":{"name":"Security \u0026 Compliance","metadata":null},"metadata":{"svgColor":"ui/public/static/img/meshmodels/identity-manager/color/workloadidentity-color.svg","svgWhite":"ui/public/static/img/meshmodels/identity-manager/white/workloadidentity-white.svg"},"duplicates":0}]}
364 changes: 364 additions & 0 deletions mesheryctl/internal/cli/root/system/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,364 @@
// Copyright 2023 Layer5, Inc.
//
// 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 system

import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"

"github.com/layer5io/meshery/mesheryctl/internal/cli/root/config"
"github.com/layer5io/meshery/mesheryctl/pkg/utils"
"github.com/layer5io/meshery/server/models"
"github.com/layer5io/meshkit/models/meshmodel/core/v1alpha1"
"github.com/manifoldco/promptui"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gopkg.in/yaml.v2"
)

var (
// flag used to specify the page number in list command
pageNumberFlag int
// flag used to specify format of output of view {model-name} command
outFormatFlag string
)

// represents the `mesheryctl system model list` subcommand.
var listModelCmd = &cobra.Command{
Use: "list",
Short: "list models",
Long: "list name of all registered models",
Example: `
// View list of models
mesheryctl system model list

// View list of models with specified page number (25 models per page)
mesheryctl system model list --page 2
`,
PreRunE: func(cmd *cobra.Command, args []string) error {
//Check prerequisite
hcOptions := &HealthCheckOptions{
IsPreRunE: true,
PrintLogs: false,
Subcommand: cmd.Use,
}
hc, err := NewHealthChecker(hcOptions)
if err != nil {
return ErrHealthCheckFailed(err)
}
// execute healthchecks
err = hc.RunPreflightHealthChecks()
if err != nil {
cmd.SilenceUsage = true
return err
}
mctlCfg, err := config.GetMesheryCtl(viper.GetViper())
if err != nil {
return err
}
ctx, err := mctlCfg.GetCurrentContext()
if err != nil {
return err
}
err = ctx.ValidateVersion()
if err != nil {
return err
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 0 {
return errors.New(utils.SystemModelSubError("this command takes no arguments\n", "list"))
}
mctlCfg, err := config.GetMesheryCtl(viper.GetViper())
if err != nil {
log.Fatalln(err, "error processing config")
}

baseUrl := mctlCfg.GetBaseMesheryURL()
var url string
if cmd.Flags().Changed("page") {
url = fmt.Sprintf("%s/api/meshmodels/models?page=%d", baseUrl, pageNumberFlag)
} else {
url = fmt.Sprintf("%s/api/meshmodels/models?pagesize=all", baseUrl)
}
req, err := utils.NewRequest(http.MethodGet, url, nil)
if err != nil {
utils.Log.Error(err)
return err
}

resp, err := utils.MakeRequest(req)
if err != nil {
utils.Log.Error(err)
return err
}

// defers the closing of the response body after its use, ensuring that the resources are properly released.
defer resp.Body.Close()

data, err := io.ReadAll(resp.Body)
if err != nil {
utils.Log.Error(err)
return err
}

modelsResponse := &models.MeshmodelsAPIResponse{}
err = json.Unmarshal(data, modelsResponse)
if err != nil {
utils.Log.Error(err)
return err
}

header := []string{"Category", "Model", "Version"}
rows := [][]string{}

for _, model := range modelsResponse.Models {
if len(model.DisplayName) > 0 {
rows = append(rows, []string{model.Category.Name, model.Name, model.Version})
}
}

if len(rows) == 0 {
// if no model is found
utils.Log.Info("No model(s) found")
} else {
utils.PrintToTable(header, rows)
}

return nil
},
}

// represents the `mesheryctl system model view [model-name]` subcommand.
var viewModelCmd = &cobra.Command{
Use: "view",
Short: "view model",
Long: "view a model queried by its name",
Example: `
// View current provider
mesheryctl system model view [model-name]
`,
PreRunE: func(cmd *cobra.Command, args []string) error {
//Check prerequisite
hcOptions := &HealthCheckOptions{
IsPreRunE: true,
PrintLogs: false,
Subcommand: cmd.Use,
}
hc, err := NewHealthChecker(hcOptions)
if err != nil {
return ErrHealthCheckFailed(err)
}
// execute healthchecks
err = hc.RunPreflightHealthChecks()
if err != nil {
cmd.SilenceUsage = true
return err
}
mctlCfg, err := config.GetMesheryCtl(viper.GetViper())
if err != nil {
return err
}
ctx, err := mctlCfg.GetCurrentContext()
if err != nil {
return err
}
err = ctx.ValidateVersion()
if err != nil {
return err
}
return nil
},
Args: func(_ *cobra.Command, args []string) error {
const errMsg = "Usage: mesheryctl system model view [model-name]\nRun 'mesheryctl system model view [model-name]' to view the model"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One line of usage for two subcommands isn't enough documentation or example, @zakisk

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still not answered...

if len(args) == 0 {
return fmt.Errorf("model name isn't specified\n\n%v", errMsg)
} else if len(args) > 1 {
return fmt.Errorf("too many arguments\n\n%v", errMsg)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
mctlCfg, err := config.GetMesheryCtl(viper.GetViper())
if err != nil {
log.Fatalln(err, "error processing config")
}

baseUrl := mctlCfg.GetBaseMesheryURL()
model := args[0]

url := fmt.Sprintf("%s/api/meshmodels/models/%s?pagesize=all", baseUrl, model)
req, err := utils.NewRequest(http.MethodGet, url, nil)
if err != nil {
utils.Log.Error(err)
return err
}

resp, err := utils.MakeRequest(req)
if err != nil {
utils.Log.Error(err)
return err
}

// defers the closing of the response body after its use, ensuring that the resources are properly released.
defer resp.Body.Close()

data, err := io.ReadAll(resp.Body)
if err != nil {
utils.Log.Error(err)
return err
}

modelsResponse := &models.MeshmodelsAPIResponse{}
err = json.Unmarshal(data, modelsResponse)
if err != nil {
utils.Log.Error(err)
return err
}

var selectedModel v1alpha1.Model

if modelsResponse.Count == 0 {
utils.Log.Info("No model(s) found for the given name ", model)
return nil
} else if modelsResponse.Count == 1 {
selectedModel = modelsResponse.Models[0]
} else {
selectedModel = selectModelPrompt(modelsResponse.Models)
}

var output []byte

// user may pass flag in lower or upper case but we have to keep it lower
// in order to make it consistent while checking output format
outFormatFlag = strings.ToLower(outFormatFlag)
if outFormatFlag == "yaml" {
if output, err = yaml.Marshal(selectedModel); err != nil {
return errors.Wrap(err, "failed to format output in YAML")
}
fmt.Print(string(output))
} else if outFormatFlag == "json" {
return outputJson(selectedModel)
} else {
return errors.New("output-format choice invalid, use [json|yaml]")
}

return nil
},
}

// providerCmd represents the `mesheryctl system model` command
var modelCmd = &cobra.Command{
Use: "model",
Short: "Generate and update components",
Long: `Generates and updates components in binary form`,
Example: `
// To view list of components
mesheryctl system model list

// To view a specific model
mesheryctl system model view [model-name]
zakisk marked this conversation as resolved.
Show resolved Hide resolved
`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New(utils.SystemModelSubError("please specify a flag or subcommand. Use 'mesheryctl system model --help' to display user guide.\n", "model"))
}
if ok := utils.IsValidSubcommand(availableSubcommands, args[0]); !ok {
return errors.New(utils.SystemModelSubError(fmt.Sprintf("'%s' is an invalid subcommand. Please provide required options from [view]. Use 'mesheryctl system model --help' to display usage guide.\n", args[0]), "model"))
}
_, err := config.GetMesheryCtl(viper.GetViper())
if err != nil {
log.Fatalln(err, "error processing config")
}
err = viewProviderCmd.RunE(cmd, args)
if err != nil {
return err
}
err = cmd.Usage()
if err != nil {
return err
}
return nil
},
}

func init() {
listModelCmd.Flags().IntVarP(&pageNumberFlag, "page", "p", 1, "(optional) List next set of models with --page (default = 1)")
viewModelCmd.Flags().StringVarP(&outFormatFlag, "output-format", "o", "yaml", "(optional) format to display in [json|yaml]")
availableSubcommands = []*cobra.Command{listModelCmd, viewModelCmd}
modelCmd.AddCommand(availableSubcommands...)
}

// `selectModelPrompt` lets user to select a model if models are more than one
func selectModelPrompt(models []v1alpha1.Model) v1alpha1.Model {
modelArray := []v1alpha1.Model{}
modelNames := []string{}

modelArray = append(modelArray, models...)

for _, model := range modelArray {
modelName := fmt.Sprintf("%s, version: %s", model.DisplayName, model.Version)
modelNames = append(modelNames, modelName)
}

prompt := promptui.Select{
Label: "Select a model",
Items: modelNames,
}

for {
i, _, err := prompt.Run()
if err != nil {
continue
}

return modelArray[i]
}
}

func outputJson(model v1alpha1.Model) error {
if err = prettifyJson(model); err != nil {
// if prettifyJson return error, marshal output in conventional way using json.MarshalIndent
// but it doesn't convert unicode to its corresponding HTML string (it is default behavior)
// e.g unicode representation of '&' will be printed as '\u0026'
if output, err := json.MarshalIndent(model, "", " "); err != nil {
return errors.Wrap(err, "failed to format output in JSON")
} else {
fmt.Print(string(output))
}
}
return nil
}

// prettifyJson takes a v1alpha1.Model struct as input, marshals it into a nicely formatted JSON representation,
// and prints it to standard output with proper indentation and without escaping HTML entities.
func prettifyJson(model v1alpha1.Model) error {
// Create a new JSON encoder that writes to the standard output (os.Stdout).
enc := json.NewEncoder(os.Stdout)
// Configure the JSON encoder settings.
// SetEscapeHTML(false) prevents special characters like '<', '>', and '&' from being escaped to their HTML entities.
enc.SetEscapeHTML(false)
enc.SetIndent("", " ")

// Any errors during the encoding process will be returned as an error.
return enc.Encode(model)
}