1package v2 2 3import ( 4 "crypto/rand" 5 "encoding/hex" 6 "errors" 7 "net/url" 8 "path" 9 "regexp" 10 "strings" 11 12 "github.com/sensu/sensu-go/js" 13) 14 15const ( 16 // AssetsResource is the name of this resource type 17 AssetsResource = "assets" 18) 19 20var ( 21 // AssetNameRegexStr used to validate name of asset 22 AssetNameRegexStr = `[a-z0-9\/\_\.\-]+` 23 24 // AssetNameRegex used to validate name of asset 25 AssetNameRegex = regexp.MustCompile("^" + AssetNameRegexStr + "$") 26) 27 28// StorePrefix returns the path prefix to this resource in the store 29func (a *Asset) StorePrefix() string { 30 return AssetsResource 31} 32 33// URIPath returns the path component of an asset URI. 34func (a *Asset) URIPath() string { 35 return path.Join(URLPrefix, "namespaces", url.PathEscape(a.Namespace), AssetsResource, url.PathEscape(a.Name)) 36} 37 38// Validate returns an error if the asset contains invalid values. 39func (a *Asset) Validate() error { 40 if err := ValidateAssetName(a.Name); err != nil { 41 return err 42 } 43 44 if a.Namespace == "" { 45 return errors.New("namespace cannot be empty") 46 } 47 48 if a.Sha512 == "" { 49 return errors.New("SHA-512 checksum cannot be empty") 50 } 51 52 if len(a.Sha512) < 128 { 53 return errors.New("SHA-512 checksum must be at least 128 characters") 54 } 55 56 if a.URL == "" { 57 return errors.New("URL cannot be empty") 58 } 59 60 u, err := url.Parse(a.URL) 61 if err != nil { 62 return errors.New("invalid URL provided") 63 } 64 65 if u.Scheme != "https" && u.Scheme != "http" { 66 return errors.New("URL must be HTTP or HTTPS") 67 } 68 69 return js.ParseExpressions(a.Filters) 70} 71 72// ValidateAssetName validates that asset's name is valid 73func ValidateAssetName(name string) error { 74 if name == "" { 75 return errors.New("name cannot be empty") 76 } 77 78 if !AssetNameRegex.MatchString(name) { 79 return errors.New( 80 "name must be lowercase and may only contain forward slashes, underscores, dashes and numbers", 81 ) 82 } 83 84 return nil 85} 86 87// Filename returns the filename of the underlying asset; pulled from the URL 88func (a *Asset) Filename() string { 89 u, err := url.Parse(a.URL) 90 if err != nil { 91 return "" 92 } 93 94 _, file := path.Split(u.EscapedPath()) 95 return file 96} 97 98// FixtureAsset given a name returns a valid asset for use in tests 99func FixtureAsset(name string) *Asset { 100 bytes := make([]byte, 10) 101 _, _ = rand.Read(bytes) 102 hash := hex.EncodeToString(bytes) 103 104 asset := &Asset{ 105 ObjectMeta: NewObjectMeta(name, "default"), 106 Sha512: "25e01b962045f4f5b624c3e47e782bef65c6c82602524dc569a8431b76cc1f57639d267380a7ec49f70876339ae261704fc51ed2fc520513cf94bc45ed7f6e17", 107 URL: "https://localhost/" + hash + ".zip", 108 } 109 return asset 110} 111 112// NewAsset creates a new Asset. 113func NewAsset(meta ObjectMeta) *Asset { 114 return &Asset{ObjectMeta: meta} 115} 116 117// AssetFields returns a set of fields that represent that resource 118func AssetFields(r Resource) map[string]string { 119 resource := r.(*Asset) 120 return map[string]string{ 121 "asset.name": resource.ObjectMeta.Name, 122 "asset.namespace": resource.ObjectMeta.Namespace, 123 "asset.filters": strings.Join(resource.Filters, ","), 124 } 125} 126 127// SetNamespace sets the namespace of the resource. 128func (a *Asset) SetNamespace(namespace string) { 129 a.Namespace = namespace 130} 131