func (e *Extension) getKustomizePath(dirPath string) (string, error) {
	dirPath, err := e.getDirectoryPath(dirPath)
	if err != nil {
		return "", err
	}
	u, err := url.Parse(dirPath)
	if err != nil || !u.IsAbs() || u.Host != "raw.githubusercontent.com" {
		return dirPath, nil
	}
	u.Host = "github.com"
	urlPathTokens := strings.Split(u.Path, "/")
	u.RawQuery = fmt.Sprintf("ref=%s", urlPathTokens[3])
	u.Path = strings.Join(append(urlPathTokens[:3], urlPathTokens[4:]...), "/")
	return u.String(), nil
}
Construct a Kustomize path based on the input dirPath and the
extension repository path. The input dirPath can be an absolute
(local) path, a URL, or a path relative to the extension repository
path or URL
package deployments
import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"
	"github.com/ghodss/yaml"
	"github.com/pkg/errors"
	"github.com/fuseml/fuseml/cli/helpers"
	"github.com/fuseml/fuseml/cli/kubernetes"
	"github.com/fuseml/fuseml/cli/paas/ui"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
	defaultDescriptionFileName = "description.yaml"
	defaultNamespace           = "fuseml-workloads"
	tmpSubDir                  = "fuseml-extension"
)
type waitForStep struct {
	Kind      string
	Namespace string
	Condition string
	Selector  string
	Timeout   int
}
type installStep struct {
	Type      string
	Location  string
	Repo      string
	Chart     string
	Values    string
	Version   string
	Namespace string
	WaitFor   []waitForStep
}
type istioGateway struct {
	Namespace   string
	Name        string
	Port        int
	HostPrefix  string
	ServiceHost string
}
type registeredExtensionService struct {
	ID           *string
	ExtensionID  *string
	Resource     *string
	Category     *string
	Description  *string
	AuthRequired *bool
	Status       *registeredExtensionServiceStatus
	Endpoints    []*registeredExtensionEndpoint
	Credentials  []*registeredExtensionCredentials
}
type registeredExtensionQuery struct {
}
type registeredExtension struct {
	ID            *string
	Product       *string
	Version       *string
	Description   *string
	Zone          *string
	Configuration map[string]string
	Status        *registeredExtensionStatus
	Services      []*registeredExtensionService
}
type registeredExtensionEndpoint struct {
	URL           *string
	ExtensionID   *string
	ServiceID     *string
	Type          *string
	Configuration map[string]string
	Status        *registeredExtensionEndpointStatus
}
type registeredExtensionCredentials struct {
	ID            *string
	ExtensionID   *string
	ServiceID     *string
	Default       *bool
	Scope         *string
	Projects      []string
	Users         []string
	Configuration map[string]string
	Status        *registeredExtensionCredentialsStatus
}
type registeredExtensionCredentialsStatus struct {
	Created string
	Updated string
}
type registeredExtensionStatus struct {
	Registered string
	Updated    string
}
type registeredExtensionServiceStatus struct {
	Registered string
	Updated    string
}
type registeredExtensionEndpointStatus struct {
}
type serviceCredentialTemplate struct {
	ServiceID   string
	Credentials []credentialTemplate
}
type credentialTemplate struct {
	ID        string
	Transform []credentialTransformValue
}
type credentialTransformValue struct {
	ConfigValue string
	SecretValue string
	Secret      string
	Namespace   string
}
type extensionDesc struct {
	Name               string
	Product            string
	Version            string
	Description        string
	Namespace          string
	Zone               string
	Requires           []string
	Install            []installStep
	Uninstall          []installStep
	Gateways           []istioGateway
	Services           []registeredExtensionService
	ServiceCredentials []serviceCredentialTemplate
	RoleRules          []roleRule
}
type roleRule struct {
	ApiGroups []string
	Resources []string
	Verbs     []string
}
type Extension struct {
	Name                   string
	Repository             string
	Debug                  bool
	Timeout                int
	Desc                   *extensionDesc
	TransformedCredentials map[string]map[string]map[string]string
}
func NewExtension(name, repository string, timeout int) *Extension {
	return &Extension{
		Name:       name,
		Repository: repository,
		Desc:       &extensionDesc{},
		Debug:      false,
		Timeout:    timeout,
	}
}
func (e *Extension) LoadDescription() error {
	u, err := url.Parse(e.Repository)
	if err != nil {
		return err
	}
	tmpDir, err := ioutil.TempDir("", tmpSubDir)
	if err != nil {
		return errors.Wrap(err, "can't create temp directory "+tmpDir)
	}
	defer os.RemoveAll(tmpDir)
	descFilePath := ""
	if u.IsAbs() && u.Scheme != "" && u.Host != "" {
		u, _ = u.Parse(e.Name + "/")
		u, _ = u.Parse(defaultDescriptionFileName)
		if err := helpers.DownloadFile(u.String(), defaultDescriptionFileName, tmpDir); err != nil {
			return err
		}
		descFilePath = filepath.Join(tmpDir, defaultDescriptionFileName)
	} else {
		info, err := os.Stat(e.Repository)
		if os.IsNotExist(err) {
			return err
		}
		if !info.IsDir() {
			return errors.New("Provided path to extension repository is neither URL nor a directory")
		}
		descFilePath = filepath.Join(e.Repository, e.Name, defaultDescriptionFileName)
	}
	data, err := os.ReadFile(descFilePath)
	if err != nil {
		return errors.Wrap(err, "failed to read description file")
	}
	err = yaml.Unmarshal(data, &e.Desc)
	if err != nil {
		return errors.Wrap(err, "failed to parse description file")
	}
	return nil
}
func (e *Extension) fetchFile(filePath, tmpDir string) (string, error) {
	if filepath.IsAbs(filePath) {
		return filePath, nil
	}
	name := filepath.Base(filePath)
	u, err := url.Parse(filePath)
	if err != nil {
		return "", err
	}
	if u.IsAbs() && u.Host != "" {
		if err := helpers.DownloadFile(u.String(), name, tmpDir); err != nil {
			return "", err
		}
		return filepath.Join(tmpDir, name), nil
	}
	u, err = url.Parse(e.Repository)
	if u.IsAbs() && u.Host != "" {
		u, _ = u.Parse(e.Name + "/")
		u, _ = u.Parse(filePath)
		if err := helpers.DownloadFile(u.String(), name, tmpDir); err != nil {
			return "", err
		}
		return filepath.Join(tmpDir, name), nil
	}
	return filepath.Join(e.Repository, e.Name, filePath), nil
}
func (e *Extension) getDirectoryPath(dirPath string) (string, error) {
	if filepath.IsAbs(dirPath) {
		return dirPath, nil
	}
	u, err := url.Parse(dirPath)
	if err != nil {
		return "", err
	}
	if u.IsAbs() && u.Host != "" {
		return dirPath, nil
	}
	if filepath.IsAbs(e.Repository) {
		return filepath.Join(e.Repository, e.Name, dirPath), nil
	}
	u, _ = url.Parse(e.Repository)
	u.Path = path.Join(u.Path, e.Name, dirPath)
	return u.String(), nil
}