Implementation of IPS deploy logic 91/11191/1
authorYouhwan Seol <yh.seol@samsung.com>
Wed, 8 Feb 2023 10:19:39 +0000 (19:19 +0900)
committerYouhwan Seol <yh.seol@samsung.com>
Tue, 23 May 2023 05:33:26 +0000 (14:33 +0900)
Change-Id: Ifc6ffa865b91913750331a0f2ce2bab10272a147
Signed-off-by: Youhwan Seol <yh.seol@samsung.com>
go.mod
pkg/api/v1/deployment/deployment.go
pkg/client/kserve/client.go
pkg/client/kserve/utils.go [new file with mode: 0644]
pkg/commons/types/values.go [new file with mode: 0644]
pkg/controller/v1/adapter/controller.go
pkg/controller/v1/adapter/utils.go [new file with mode: 0644]

diff --git a/go.mod b/go.mod
index 584d2b3..c01b951 100755 (executable)
--- a/go.mod
+++ b/go.mod
@@ -8,13 +8,15 @@ require (
        github.com/kserve/kserve v0.7.0
        github.com/pkg/errors v0.9.1 // indirect
        github.com/stretchr/testify v1.8.1
+       gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0
+       k8s.io/api v0.20.2
+       k8s.io/apimachinery v0.20.2
        k8s.io/client-go v11.0.1-0.20190805182717-6502b5e7b1b5+incompatible
 )
 
 require (
        github.com/spf13/viper v1.7.0
        github.com/xeipuuv/gojsonschema v1.2.0
-       gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0
 )
 
 require (
@@ -88,9 +90,7 @@ require (
        gopkg.in/ini.v1 v1.56.0 // indirect
        gopkg.in/yaml.v2 v2.4.0 // indirect
        gopkg.in/yaml.v3 v3.0.1 // indirect
-       k8s.io/api v0.20.2 // indirect
        k8s.io/apiextensions-apiserver v0.20.2 // indirect
-       k8s.io/apimachinery v0.20.2 // indirect
        k8s.io/klog v1.0.0 // indirect
        k8s.io/klog/v2 v2.4.0 // indirect
        k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 // indirect
index df1d85d..68ff9a2 100644 (file)
@@ -60,7 +60,7 @@ func (Executor) Deploy(c *gin.Context) {
                return
        }
 
-       err := ipsAdapter.Deploy(name, version)
+       _, err := ipsAdapter.Deploy(name, version)
        if err != nil {
                utils.WriteError(c.Writer, err)
                return
index 0993e5c..4d52d9f 100644 (file)
@@ -27,6 +27,7 @@ import (
 
        "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/commons/errors"
        "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/commons/logger"
+       "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/commons/types"
 )
 
 const (
@@ -37,6 +38,7 @@ var ifsvGetter func(string) (client_v1beta1.InferenceServiceInterface, error)
 
 type Command interface {
        Init(kubeconfigPath string) error
+       Create(values types.Values) (string, error)
 }
 
 type Client struct {
@@ -82,3 +84,21 @@ func (c *Client) Init(kubeconfigPath string) (err error) {
        }
        return
 }
+
+func (c *Client) Create(values types.Values) (revision string, err error) {
+       logger.Logging(logger.DEBUG, "IN")
+       defer logger.Logging(logger.DEBUG, "OUT")
+
+       info := convertValuesToInferenceService(values)
+       if err != nil {
+               return
+       }
+
+       _, err = c.api.Create(&info)
+       if err != nil {
+               logger.Logging(logger.ERROR, err.Error())
+               err = errors.InternalServerError{Message: err.Error()}
+               return
+       }
+       return
+}
diff --git a/pkg/client/kserve/utils.go b/pkg/client/kserve/utils.go
new file mode 100644 (file)
index 0000000..81fcd93
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+==================================================================================
+
+Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+
+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 kserve
+
+import (
+       "strconv"
+
+       api_v1beta1 "github.com/kserve/kserve/pkg/apis/serving/v1beta1"
+       core_v1 "k8s.io/api/core/v1"
+       v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+       "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/commons/logger"
+       "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/commons/types"
+)
+
+func convertValuesToInferenceService(values types.Values) (ifsv api_v1beta1.InferenceService) {
+       logger.Logging(logger.DEBUG, "IN")
+       defer logger.Logging(logger.DEBUG, "OUT")
+
+       maxReplicas, err := strconv.Atoi(values.MAXReplicas)
+       if err != nil {
+               logger.Logging(logger.ERROR, err.Error())
+               maxReplicas = 1
+       }
+
+       minReplicas, err := strconv.Atoi(values.MINReplicas)
+       if err != nil {
+               logger.Logging(logger.ERROR, err.Error())
+               minReplicas = 1
+       }
+
+       ifsv = api_v1beta1.InferenceService{
+               TypeMeta: v1.TypeMeta{
+                       Kind:       "InferenceService",
+                       APIVersion: "serving.kserve.io/v1beta1",
+               },
+               ObjectMeta: v1.ObjectMeta{
+                       Name:      values.FullName,
+                       Namespace: ips_namespace,
+                       Labels: map[string]string{
+                               "controller-tools.k8s.io": "1.0",
+                               "app":                     values.FullName,
+                       },
+               },
+               Spec: api_v1beta1.InferenceServiceSpec{
+                       Predictor: api_v1beta1.PredictorSpec{
+                               ComponentExtensionSpec: api_v1beta1.ComponentExtensionSpec{
+                                       MaxReplicas: maxReplicas,
+                                       MinReplicas: &minReplicas,
+                               },
+                               PodSpec: api_v1beta1.PodSpec{
+                                       ServiceAccountName: values.RICServiceAccountName,
+                               },
+                       },
+               },
+               Status: api_v1beta1.InferenceServiceStatus{},
+       }
+
+       switch values.Engine {
+       case "tensorflow":
+               ifsv.Spec.Predictor.Tensorflow = &api_v1beta1.TFServingSpec{
+                       PredictorExtensionSpec: api_v1beta1.PredictorExtensionSpec{
+                               StorageURI: &values.StorageURI,
+                               Container: core_v1.Container{
+                                       Image: values.Image,
+                                       Ports: []core_v1.ContainerPort{
+                                               {
+                                                       Name:          "h2c",
+                                                       ContainerPort: 9000,
+                                                       Protocol:      "TCP",
+                                               },
+                                       },
+                               },
+                       },
+               }
+       case "sklearn":
+               ifsv.Spec.Predictor.SKLearn = &api_v1beta1.SKLearnSpec{
+                       PredictorExtensionSpec: api_v1beta1.PredictorExtensionSpec{
+                               StorageURI: &values.StorageURI,
+                               Container: core_v1.Container{
+                                       Image: values.Image,
+                                       Ports: []core_v1.ContainerPort{
+                                               {
+                                                       Name:          "h2c",
+                                                       ContainerPort: 9000,
+                                                       Protocol:      "TCP",
+                                               },
+                                       },
+                               },
+                       },
+               }
+       }
+
+       if values.CanaryTrafficPercent >= 0 {
+               ifsv.Spec.Predictor.CanaryTrafficPercent = &values.CanaryTrafficPercent
+       }
+
+       if values.ResourceVersion != "" {
+               ifsv.ResourceVersion = values.ResourceVersion
+       }
+
+       return
+}
diff --git a/pkg/commons/types/values.go b/pkg/commons/types/values.go
new file mode 100644 (file)
index 0000000..f8a9614
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+==================================================================================
+
+Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+
+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 types
+
+type Values struct {
+       APIVersion            string `yaml:"api_version"`
+       Engine                string `yaml:"engine"`
+       Name                  string `yaml:"name"`
+       FullName              string `yaml:"fullname"`
+       ImagePullPolicy       string `yaml:"image_pull_policy"`
+       MAXReplicas           string `yaml:"max_replicas"`
+       MINReplicas           string `yaml:"min_replicas"`
+       Resources             string `yaml:"resources"`
+       RICServiceAccountName string `yaml:"ric_serviceaccount_name"`
+       StorageURI            string `yaml:"storageUri"`
+       Image                 string `yaml:"image"`
+       ResourceVersion       string
+       CanaryTrafficPercent  int64
+}
index 6506f68..1918763 100644 (file)
@@ -28,7 +28,7 @@ import (
 )
 
 type Command interface {
-       Deploy(name string, version string) error
+       Deploy(name string, version string) (string, error)
 }
 
 type Executor struct {
@@ -38,22 +38,44 @@ type Executor struct {
 var kserveClient kserve.Command
 var onboardClient onboard.Command
 
+var removeFunc func(string) error
+
 func init() {
        kserveClient = &kserve.Client{}
-       onboardClient = onboard.Executor{}
 
        kubeconfigPath := os.Getenv("KUBECONFIG")
        err := kserveClient.Init(kubeconfigPath)
        if err != nil {
                os.Exit(8)
        }
+
+       onboardClient = onboard.Executor{}
+
+       removeFunc = func(path string) (err error) {
+               err = os.RemoveAll(path)
+               return
+       }
 }
 
-func (Executor) Deploy(name string, version string) error {
+func (Executor) Deploy(name string, version string) (revision string, err error) {
        logger.Logging(logger.DEBUG, "IN")
        defer logger.Logging(logger.DEBUG, "OUT")
 
-       // TODO: Get object from onboard & Deploy using kserveClient
+       path, err := onboardClient.Download(name, version)
+       if err != nil {
+               return
+       }
+       defer removeFunc(path)
+
+       values, err := valueParse(path)
+       if err != nil {
+               logger.Logging(logger.ERROR, err.Error())
+               return
+       }
 
-       return nil
+       revision, err = kserveClient.Create(values)
+       if err != nil {
+               return
+       }
+       return
 }
diff --git a/pkg/controller/v1/adapter/utils.go b/pkg/controller/v1/adapter/utils.go
new file mode 100644 (file)
index 0000000..52605c6
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+==================================================================================
+
+Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
+
+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 adapter
+
+import (
+       "os"
+       "path/filepath"
+
+       "gopkg.in/yaml.v1"
+
+       "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/commons/errors"
+       "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/commons/logger"
+       "gerrit.o-ran-sc.org/r/aiml-fw/aihp/ips/kserve-adapter/pkg/commons/types"
+)
+
+func valueParse(path string) (values types.Values, err error) {
+       logger.Logging(logger.DEBUG, "IN")
+       defer logger.Logging(logger.DEBUG, "OUT")
+
+       filePath, err := getValuesFilePath(path)
+       if err != nil {
+               return
+       }
+
+       b, err := os.ReadFile(filePath)
+       if err != nil {
+               logger.Logging(logger.ERROR, err.Error())
+               err = errors.IOError{Message: err.Error()}
+               return
+       }
+
+       err = yaml.Unmarshal(b, &values)
+       if err != nil {
+               logger.Logging(logger.ERROR, err.Error())
+               err = errors.IOError{Message: err.Error()}
+               return
+       }
+       values.CanaryTrafficPercent = -1
+
+       return
+}
+
+func getValuesFilePath(root string) (path string, err error) {
+       logger.Logging(logger.DEBUG, "IN")
+       defer logger.Logging(logger.DEBUG, "OUT")
+
+       files, err := os.ReadDir(root)
+       if err != nil {
+               logger.Logging(logger.ERROR, err.Error())
+               return "", errors.IOError{Message: err.Error()}
+       }
+
+       for _, file := range files {
+               if file.IsDir() {
+                       path = filepath.Join(root, file.Name())
+               }
+       }
+       path = filepath.Join(path, "values.yaml")
+       return
+}