Skip to content

Commit

Permalink
Merge branch 'master' into model_export
Browse files Browse the repository at this point in the history
Signed-off-by: Nithish Karthik <[email protected]>
  • Loading branch information
humblenginr authored Jul 9, 2024
2 parents 2d76479 + 0a543e8 commit ff1959c
Show file tree
Hide file tree
Showing 12 changed files with 444 additions and 3 deletions.
7 changes: 7 additions & 0 deletions docs/_data/mesheryctlcommands/cmds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,13 @@ model:
output-format:
name: -o, --output-format
description: (optional) format to display in [json|yaml] (default "yaml")
import:
name: import
description: Import models using a file or filepath directly to the meshery registry
usage: mesheryctl model import [ file | filepath ]
examples:
- import model /path/to/[file.yaml|file.json]
- import model /path/to/models

components:
name: components
Expand Down
1 change: 1 addition & 0 deletions docs/pages/project/contributing/ci-build-and-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Some portions of the workflow require secrets to accomplish their tasks. These s
- `SLACK_BOT_TOKEN`: Used for notification of new GitHub stars given to the Meshery repo.
- `CYPRESS_RECORD_KEY`: Used for integration with the Layer5 account on Cypress.
- `GLOBAL_TOKEN`: Used for securely transmitting performance test results for the None Provider.
- `NPM_TOKEN`: npm authentication token, used to perform authentication against the npm registry in meshery deployment workflow.

The Docker Hub user, `mesheryci`, belongs to the "ciusers" team in Docker Hub and acts as the service account under which these automated builds are being pushed. Every time a new Docker Hub repository is created we have to grant “Admin” (in order to update the README in the Docker Hub repository) permissions to the ciusers team.

Expand Down
2 changes: 1 addition & 1 deletion docs/pages/reference/mesheryctl-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ Installation, troubleshooting and debugging of Meshery and its adapters
</tr>
{% assign command12 = site.data.mesheryctlcommands.cmds.model %}
<tr>
<td rowspan=8><a href="{{ site.baseurl }}/reference/mesheryctl/{{ command12.name }}">{{ command12.name }}</a></td>
<td rowspan=9><a href="{{ site.baseurl }}/reference/mesheryctl/{{ command12.name }}">{{ command12.name }}</a></td>
<td></td>
<td></td>
<td>{{ command12.description }}</td>
Expand Down
218 changes: 218 additions & 0 deletions mesheryctl/internal/cli/root/model/import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package model

import (
"bytes"
"compress/gzip"
"errors"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"

"github.com/layer5io/meshery/mesheryctl/internal/cli/root/config"
"github.com/layer5io/meshery/mesheryctl/pkg/utils"
"github.com/layer5io/meshery/server/handlers"
"github.com/layer5io/meshery/server/models"
meshkitutils "github.com/layer5io/meshkit/utils"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var importModelCmd = &cobra.Command{
Use: "import",
Short: "import models from mesheryctl command",
Long: "import model by specifying the directory, file. Use 'import model [filepath]' or 'import model [directory]'.",
Example: `
import model /path/to/[file.yaml|file.json]
import model /path/to/models
`,
PreRunE: func(cmd *cobra.Command, args []string) error {
// Check prerequisites
mctlCfg, err := config.GetMesheryCtl(viper.GetViper())
if err != nil {
return err
}
err = utils.IsServerRunning(mctlCfg.GetBaseMesheryURL())
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 model import [ file | filePath ]\nRun 'mesheryctl model import --help' to see detailed help message"
if len(args) == 0 {
return fmt.Errorf("[ file | filepath ] 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 {
path := args[0]
fileInfo, err := os.Stat(path)
if err != nil {
utils.Log.Error(meshkitutils.ErrReadDir(err, path))
return err
}
if fileInfo.IsDir() {
tarData, err := compressDirectory(path)
if err != nil {
return err
}
fileName := filepath.Base(path) + ".tar.gz"
err = sendToAPI(tarData, fileName, "dir")
if err != nil {
utils.Log.Error(err)
return err
}
} else {
if meshkitutils.IsYaml(path) {
fileData, err := os.ReadFile(path)
if err != nil {
utils.Log.Error(meshkitutils.ErrReadFile(err, path))
return nil
}
err = sendToAPI(fileData, path, "file")
if err != nil {
utils.Log.Error(err)
return nil
}
} else if meshkitutils.IsTarGz(path) || meshkitutils.IsZip(path) {
fileData, err := os.ReadFile(path)
if err != nil {
err = meshkitutils.ErrReadFile(err, path)
utils.Log.Error(err)
return nil
}
err = sendToAPI(fileData, path, "dir")
if err != nil {
utils.Log.Error(err)
return nil
}
} else {
err = utils.ErrInvalidFile(errors.New("invalid file format"))
utils.Log.Error(err)
return nil
}
}
return nil
},
}

func compressDirectory(dirpath string) ([]byte, error) {
tw := meshkitutils.NewTarWriter()
defer tw.Close()

err := filepath.Walk(dirpath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return meshkitutils.ErrFileWalkDir(err, path)
}

if info.IsDir() {
return nil
}

file, err := os.Open(path)
if err != nil {
return handlers.ErrOpenFile(path)
}
defer file.Close()

fileData, err := io.ReadAll(file)
if err != nil {
return meshkitutils.ErrReadFile(err, path)
}

relPath, err := filepath.Rel(filepath.Dir(dirpath), path)
if err != nil {
return meshkitutils.ErrRelPath(err, path)
}

if err := tw.Compress(relPath, fileData); err != nil {
return err
}

return nil
})

if err != nil {
return nil, err
}

var buf bytes.Buffer
gzipWriter := gzip.NewWriter(&buf)
_, err = io.Copy(gzipWriter, tw.Buffer)
if err != nil {
return nil, meshkitutils.ErrCopyFile(err)
}
if err := gzipWriter.Close(); err != nil {
return nil, meshkitutils.ErrCloseFile(err)
}

return buf.Bytes(), nil
}

func sendToAPI(data []byte, name string, dataType string) error {
mctlCfg, err := config.GetMesheryCtl(viper.GetViper())
if err != nil {
return err
}

baseURL := mctlCfg.GetBaseMesheryURL()
url := baseURL + "/api/meshmodels/register"
var b bytes.Buffer
writer := multipart.NewWriter(&b)

var formFile io.Writer
if dataType == "file" {
formFile, err = writer.CreateFormFile("file", filepath.Base(name))
} else {
formFile, err = writer.CreateFormField("dir")
}
if err != nil {

}
_, err = formFile.Write(data)
if err != nil {
err = meshkitutils.ErrWriteFile(err, name)
return err
}

_ = writer.Close()

req, err := utils.NewRequest(http.MethodPost, url, &b)
if err != nil {
return err
}
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, err := utils.MakeRequest(req)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
err = models.ErrDoRequest(err, resp.Request.Method, url)
return err
}
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
err = models.ErrDataRead(err, "response body")
return err
}

bodyString := string(bodyBytes)
utils.Log.Info(bodyString)
utils.Log.Info("Models imported successfully")
return nil
}
2 changes: 1 addition & 1 deletion mesheryctl/internal/cli/root/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ var (
// Color for the whiteboard printer
whiteBoardPrinter = color.New(color.FgHiBlack, color.BgWhite, color.Bold)

availableSubcommands = []*cobra.Command{listModelCmd, viewModelCmd, searchModelCmd, exportModal}
availableSubcommands = []*cobra.Command{listModelCmd, viewModelCmd, searchModelCmd, importModelCmd, exportModal}

countFlag bool
)
Expand Down
4 changes: 4 additions & 0 deletions mesheryctl/pkg/utils/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ func SystemModelSubError(msg string, cmd string) string {
return formatError(msg, cmdModelList)
case "view":
return formatError(msg, cmdModelView)
case "import":
return formatError(msg, cmdModelImport)
default:
return formatError(msg, cmdModel)
}
Expand Down Expand Up @@ -315,6 +317,8 @@ func formatError(msg string, cmd cmdType) string {
return formatUsageDetails(msg, modelUsageURL)
case cmdModelList:
return formatUsageDetails(msg, modelListURL)
case cmdModelImport:
return formatUsageDetails(msg, modelImportURl)
case cmdModelView:
return formatUsageDetails(msg, modelViewURL)
case cmdRegistry:
Expand Down
2 changes: 2 additions & 0 deletions mesheryctl/pkg/utils/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ const (
tokenUsageURL = docsBaseURL + "reference/mesheryctl/system/token"
modelUsageURL = docsBaseURL + "reference/mesheryctl/system/model"
modelListURL = docsBaseURL + "reference/mesheryctl/system/model/list"
modelImportURl = docsBaseURL + "reference/mesheryctl/system/model/import"
modelViewURL = docsBaseURL + "reference/mesheryctl/system/model/view"
registryUsageURL = docsBaseURL + "reference/mesheryctl/system/registry"
relationshipUsageURL = docsBaseURL + "reference/mesheryctl/relationships"
Expand Down Expand Up @@ -154,6 +155,7 @@ const (
cmdToken cmdType = "token"
cmdModel cmdType = "model"
cmdModelList cmdType = "model list"
cmdModelImport cmdType = "model import"
cmdModelView cmdType = "model view"
cmdRegistryPublish cmdType = "registry publish"
cmdRegistry cmdType = "regisry"
Expand Down
Loading

0 comments on commit ff1959c

Please sign in to comment.