github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/build/kubefile/parser/utils.go (about)

     1  // Copyright © 2022 Alibaba Group Holding Ltd.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package parser
    16  
    17  import (
    18  	"fmt"
    19  	"io/fs"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  
    24  	"github.com/sealerio/sealer/pkg/define/application"
    25  	osi "github.com/sealerio/sealer/utils/os"
    26  
    27  	"github.com/sirupsen/logrus"
    28  )
    29  
    30  const (
    31  	schemeLocal = "local://"
    32  	schemeHTTP  = "http://"
    33  	schemeHTTPS = "https://"
    34  )
    35  
    36  func mergeLines(lines ...string) string {
    37  	return strings.Join(lines, "\n")
    38  }
    39  
    40  func makeItDir(str string) string {
    41  	if !strings.HasSuffix(str, "/") {
    42  		return str + "/"
    43  	}
    44  	return str
    45  }
    46  
    47  func isLocal(str string) bool {
    48  	return strings.HasPrefix(str, schemeLocal)
    49  }
    50  
    51  func trimLocal(str string) string {
    52  	return strings.TrimPrefix(str, schemeLocal)
    53  }
    54  
    55  func isRemote(str string) bool {
    56  	return strings.HasPrefix(str, schemeHTTP) || strings.HasPrefix(str, schemeHTTPS)
    57  }
    58  
    59  func isHelm(sources ...string) (bool, error) {
    60  	isChartsArtifactEnough := func(path string) bool {
    61  		return osi.IsFileExist(filepath.Join(path, "Chart.yaml")) &&
    62  			osi.IsFileExist(filepath.Join(path, "values.yaml")) &&
    63  			osi.IsFileExist(filepath.Join(path, "templates"))
    64  	}
    65  
    66  	isHelmInDir := func(dirPath string) (bool, error) {
    67  		isH := false
    68  		err := filepath.WalkDir(dirPath, func(path string, d fs.DirEntry, err error) error {
    69  			if err != nil {
    70  				return err
    71  			}
    72  
    73  			isH = isChartsArtifactEnough(path)
    74  			return filepath.SkipDir
    75  		})
    76  
    77  		if err == filepath.SkipDir {
    78  			err = nil
    79  		}
    80  
    81  		return isH, err
    82  	}
    83  
    84  	chartInTargetsRoot := 0
    85  	oneOfChartsArtifact := func(str string) {
    86  		switch {
    87  		case strings.HasSuffix(str, "Chart.yaml"):
    88  			chartInTargetsRoot |= 1
    89  		case strings.HasSuffix(str, "values.yaml"):
    90  			chartInTargetsRoot |= 2
    91  		case strings.HasSuffix(str, "templates"):
    92  			chartInTargetsRoot |= 4
    93  		}
    94  	}
    95  
    96  	for _, source := range sources {
    97  		s, err := os.Stat(source)
    98  		if err != nil {
    99  			return false, fmt.Errorf("failed to stat %s: %v", source, err)
   100  		}
   101  
   102  		if s.IsDir() {
   103  			if isH, err := isHelmInDir(source); err != nil {
   104  				return false, fmt.Errorf("error in calling isHelmInDir: %v", err)
   105  			} else if isH {
   106  				return true, nil
   107  			}
   108  
   109  			files, err := os.ReadDir(source)
   110  			if err != nil {
   111  				return false, fmt.Errorf("failed to read dir (%s) in isHelm: %s", source, err)
   112  			}
   113  			for _, f := range files {
   114  				if !f.IsDir() {
   115  					oneOfChartsArtifact(f.Name())
   116  				}
   117  			}
   118  		} else {
   119  			oneOfChartsArtifact(source)
   120  		}
   121  	}
   122  
   123  	return chartInTargetsRoot == 7, nil
   124  }
   125  
   126  // getApplicationType:
   127  // walk through files to copy,try to find obvious application type,
   128  // we only support helm,kube,shell at present, and it is a strict match file suffix.
   129  // if not found, will return "".
   130  func getApplicationType(sources []string) (string, error) {
   131  	isHelmType, helmErr := isHelm(sources...)
   132  	if helmErr != nil {
   133  		return "", helmErr
   134  	}
   135  
   136  	if isHelmType {
   137  		return application.HelmApp, nil
   138  	}
   139  
   140  	appTypeFunc := func(fileName string) string {
   141  		ext := strings.ToLower(filepath.Ext(fileName))
   142  		if ext == ".sh" {
   143  			return application.ShellApp
   144  		}
   145  
   146  		if ext == ".yaml" || ext == ".yml" {
   147  			return application.KubeApp
   148  		}
   149  
   150  		return application.UnknownApp
   151  	}
   152  
   153  	var appTypeList []string
   154  	for _, source := range sources {
   155  		s, err := os.Stat(source)
   156  		if err != nil {
   157  			return "", fmt.Errorf("failed to stat %s: %v", source, err)
   158  		}
   159  
   160  		// get app type by dir
   161  		if s.IsDir() {
   162  			err = filepath.Walk(source, func(path string, f fs.FileInfo, err error) error {
   163  				if err != nil {
   164  					return err
   165  				}
   166  				if f.IsDir() {
   167  					return nil
   168  				}
   169  
   170  				appTypeList = append(appTypeList, appTypeFunc(f.Name()))
   171  				return nil
   172  			})
   173  
   174  			if err != nil {
   175  				return "", fmt.Errorf("failed to walk copy dir %s: %v", source, err)
   176  			}
   177  			continue
   178  		}
   179  
   180  		// get app type by file
   181  		appTypeList = append(appTypeList, appTypeFunc(source))
   182  	}
   183  
   184  	//matches the file suffix strictly
   185  	var isShell, isKube bool
   186  	for _, appType := range appTypeList {
   187  		if appType == application.UnknownApp {
   188  			logrus.Debugf("application type not detected in %s,%s,%s",
   189  				application.KubeApp, application.HelmApp, application.ShellApp)
   190  			return "", nil
   191  		}
   192  
   193  		if appType == application.ShellApp {
   194  			isShell = true
   195  		}
   196  
   197  		if appType == application.KubeApp {
   198  			isKube = true
   199  		}
   200  	}
   201  
   202  	if isShell && !isKube {
   203  		return application.ShellApp, nil
   204  	}
   205  
   206  	if isKube && !isShell {
   207  		return application.KubeApp, nil
   208  	}
   209  
   210  	return "", nil
   211  }
   212  
   213  // getApplicationFiles: get application files
   214  func getApplicationFiles(appName, appType string, sources []string) ([]string, error) {
   215  	if appType == "" {
   216  		return nil, nil
   217  	}
   218  
   219  	if appType == application.HelmApp {
   220  		return []string{appName}, nil
   221  	}
   222  
   223  	var launchFiles []string
   224  
   225  	for _, source := range sources {
   226  		s, err := os.Stat(source)
   227  		if err != nil {
   228  			return nil, fmt.Errorf("failed to stat %s: %v", source, err)
   229  		}
   230  
   231  		// get app launchFile if source is a dir
   232  		if s.IsDir() {
   233  			err = filepath.Walk(source, func(path string, f fs.FileInfo, err error) error {
   234  				if err != nil {
   235  					return err
   236  				}
   237  
   238  				if f.IsDir() {
   239  					return nil
   240  				}
   241  
   242  				launchFiles = append(launchFiles, strings.TrimPrefix(path, source))
   243  				return nil
   244  			})
   245  			if err != nil {
   246  				return nil, fmt.Errorf("failed to walk application dir %s: %v", source, err)
   247  			}
   248  
   249  			// if type is shell, only use first build context
   250  			if appType == application.ShellApp {
   251  				return launchFiles, nil
   252  			}
   253  			continue
   254  		}
   255  		// get app launchFile if source is a file
   256  		launchFiles = append(launchFiles, filepath.Base(source))
   257  
   258  		// if type is shell, only use first build context
   259  		if appType == application.ShellApp {
   260  			return launchFiles, nil
   261  		}
   262  	}
   263  
   264  	return launchFiles, nil
   265  }