code.gitea.io/gitea@v1.19.3/modules/packages/helm/metadata.go (about)

     1  // Copyright 2022 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package helm
     5  
     6  import (
     7  	"archive/tar"
     8  	"compress/gzip"
     9  	"io"
    10  	"strings"
    11  
    12  	"code.gitea.io/gitea/modules/util"
    13  	"code.gitea.io/gitea/modules/validation"
    14  
    15  	"github.com/hashicorp/go-version"
    16  	"gopkg.in/yaml.v3"
    17  )
    18  
    19  var (
    20  	// ErrMissingChartFile indicates a missing Chart.yaml file
    21  	ErrMissingChartFile = util.NewInvalidArgumentErrorf("Chart.yaml file is missing")
    22  	// ErrInvalidName indicates an invalid package name
    23  	ErrInvalidName = util.NewInvalidArgumentErrorf("package name is invalid")
    24  	// ErrInvalidVersion indicates an invalid package version
    25  	ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid")
    26  	// ErrInvalidChart indicates an invalid chart
    27  	ErrInvalidChart = util.NewInvalidArgumentErrorf("chart is invalid")
    28  )
    29  
    30  // Metadata for a Chart file. This models the structure of a Chart.yaml file.
    31  type Metadata struct {
    32  	APIVersion   string            `json:"api_version" yaml:"apiVersion"`
    33  	Type         string            `json:"type,omitempty" yaml:"type,omitempty"`
    34  	Name         string            `json:"name" yaml:"name"`
    35  	Version      string            `json:"version" yaml:"version"`
    36  	AppVersion   string            `json:"app_version,omitempty" yaml:"appVersion,omitempty"`
    37  	Home         string            `json:"home,omitempty" yaml:"home,omitempty"`
    38  	Sources      []string          `json:"sources,omitempty" yaml:"sources,omitempty"`
    39  	Description  string            `json:"description,omitempty" yaml:"description,omitempty"`
    40  	Keywords     []string          `json:"keywords,omitempty" yaml:"keywords,omitempty"`
    41  	Maintainers  []*Maintainer     `json:"maintainers,omitempty" yaml:"maintainers,omitempty"`
    42  	Icon         string            `json:"icon,omitempty" yaml:"icon,omitempty"`
    43  	Condition    string            `json:"condition,omitempty" yaml:"condition,omitempty"`
    44  	Tags         string            `json:"tags,omitempty" yaml:"tags,omitempty"`
    45  	Deprecated   bool              `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
    46  	Annotations  map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
    47  	KubeVersion  string            `json:"kube_version,omitempty" yaml:"kubeVersion,omitempty"`
    48  	Dependencies []*Dependency     `json:"dependencies,omitempty" yaml:"dependencies,omitempty"`
    49  }
    50  
    51  type Maintainer struct {
    52  	Name  string `json:"name,omitempty" yaml:"name,omitempty"`
    53  	Email string `json:"email,omitempty" yaml:"email,omitempty"`
    54  	URL   string `json:"url,omitempty" yaml:"url,omitempty"`
    55  }
    56  
    57  type Dependency struct {
    58  	Name         string        `json:"name" yaml:"name"`
    59  	Version      string        `json:"version,omitempty" yaml:"version,omitempty"`
    60  	Repository   string        `json:"repository" yaml:"repository"`
    61  	Condition    string        `json:"condition,omitempty" yaml:"condition,omitempty"`
    62  	Tags         []string      `json:"tags,omitempty" yaml:"tags,omitempty"`
    63  	Enabled      bool          `json:"enabled,omitempty" yaml:"enabled,omitempty"`
    64  	ImportValues []interface{} `json:"import_values,omitempty" yaml:"import-values,omitempty"`
    65  	Alias        string        `json:"alias,omitempty" yaml:"alias,omitempty"`
    66  }
    67  
    68  // ParseChartArchive parses the metadata of a Helm archive
    69  func ParseChartArchive(r io.Reader) (*Metadata, error) {
    70  	gzr, err := gzip.NewReader(r)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	defer gzr.Close()
    75  
    76  	tr := tar.NewReader(gzr)
    77  	for {
    78  		hd, err := tr.Next()
    79  		if err == io.EOF {
    80  			break
    81  		}
    82  		if err != nil {
    83  			return nil, err
    84  		}
    85  
    86  		if hd.Typeflag != tar.TypeReg {
    87  			continue
    88  		}
    89  
    90  		if hd.FileInfo().Name() == "Chart.yaml" {
    91  			if strings.Count(hd.Name, "/") != 1 {
    92  				continue
    93  			}
    94  
    95  			return ParseChartFile(tr)
    96  		}
    97  	}
    98  
    99  	return nil, ErrMissingChartFile
   100  }
   101  
   102  // ParseChartFile parses a Chart.yaml file to retrieve the metadata of a Helm chart
   103  func ParseChartFile(r io.Reader) (*Metadata, error) {
   104  	var metadata *Metadata
   105  	if err := yaml.NewDecoder(r).Decode(&metadata); err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	if metadata.APIVersion == "" {
   110  		return nil, ErrInvalidChart
   111  	}
   112  
   113  	if metadata.Type != "" && metadata.Type != "application" && metadata.Type != "library" {
   114  		return nil, ErrInvalidChart
   115  	}
   116  
   117  	if metadata.Name == "" {
   118  		return nil, ErrInvalidName
   119  	}
   120  
   121  	if _, err := version.NewSemver(metadata.Version); err != nil {
   122  		return nil, ErrInvalidVersion
   123  	}
   124  
   125  	if !validation.IsValidURL(metadata.Home) {
   126  		metadata.Home = ""
   127  	}
   128  
   129  	return metadata, nil
   130  }