vaguerent/internal/core/box_metadata.go

248 lines
5.7 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package core
import (
"encoding/json"
"io/ioutil"
"net/http"
"reflect"
"github.com/hashicorp/go-version"
"github.com/hashicorp/vagrant-plugin-sdk/core"
"github.com/mitchellh/mapstructure"
)
type BoxVersionProvider struct {
Name string
Url string
Checksum string
ChecksumType string
}
func (b *BoxVersionProvider) MatchesAny(p ...*BoxVersionProvider) (matches bool) {
for _, provider := range p {
if b.Matches(provider) {
return true
}
}
return false
}
func (b *BoxVersionProvider) Matches(p *BoxVersionProvider) (matches bool) {
pVal := reflect.ValueOf(*p)
bVal := reflect.ValueOf(*b)
typeOfMatcher := pVal.Type()
matches = true
fields := pVal.NumField()
for i := 0; i < fields; i++ {
if pVal.Field(i).Interface() != "" {
bField := bVal.FieldByName(typeOfMatcher.Field(i).Name).Interface()
pField := pVal.Field(i).Interface()
if pField != bField {
return false
}
}
}
return
}
type BoxVersion struct {
Version string
Status string
Description string
Providers []*BoxVersionProvider
}
func (b *BoxVersion) Provider(name string) (p *BoxVersionProvider, err error) {
for _, provider := range b.Providers {
if provider.Name == name {
return provider, nil
}
}
return
}
func (b *BoxVersion) ListProviders() ([]string, error) {
p := []string{}
for _, provider := range b.Providers {
p = append(p, provider.Name)
}
return p, nil
}
type BoxMetadata struct {
Name string
Description string
Versions []*BoxVersion
}
func LoadBoxMetadata(data []byte) (*BoxMetadata, error) {
var metadata map[string]interface{}
if err := json.Unmarshal(data, &metadata); err != nil {
return nil, err
}
var result BoxMetadata
err := mapstructure.Decode(metadata, &result)
return &result, err
}
func (b *BoxMetadata) matches(version string, name string, p *core.BoxProvider) (matches bool, err error) {
ver, err := b.version(version, &core.BoxProvider{Name: name})
if err != nil {
return false, err
}
provider, err := ver.Provider(name)
if err != nil {
return false, err
}
var boxVersionProvider *BoxVersionProvider
mapstructure.Decode(p, &boxVersionProvider)
return provider.Matches(boxVersionProvider), nil
}
func (b *BoxMetadata) matchesAny(version string, name string, p ...*core.BoxProvider) (matches bool, err error) {
for _, provider := range p {
m, err := b.matches(version, name, provider)
if err != nil {
return false, err
}
if m {
return true, nil
}
}
return false, nil
}
func (b *BoxMetadata) version(ver string, providerOpts *core.BoxProvider) (v *BoxVersion, err error) {
matchesProvider := false
inputVersion, err := version.NewConstraint(ver)
if err != nil {
return nil, err
}
for _, boxVer := range b.Versions {
boxVersion, err := version.NewVersion(boxVer.Version)
if err != nil {
return nil, err
}
if inputVersion.Check(boxVersion) {
// Check for the provider in the version
if providerOpts == nil {
matchesProvider = true
} else {
for _, p := range boxVer.Providers {
boxVersionProvider := &BoxVersionProvider{
Name: providerOpts.Name, Url: providerOpts.Url, Checksum: providerOpts.Checksum,
ChecksumType: providerOpts.ChecksumType,
}
if p.Matches(boxVersionProvider) {
matchesProvider = true
}
}
}
if matchesProvider {
return boxVer, nil
}
}
}
return
}
func (b *BoxMetadata) LoadMetadata(url string) (err error) {
client := &http.Client{}
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Accept", "application/json")
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
raw, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
var metadata map[string]interface{}
if err := json.Unmarshal(raw, &metadata); err != nil {
return err
}
err = mapstructure.Decode(metadata, &b)
return
}
func (b *BoxMetadata) BoxName() string {
return b.Name
}
func (b *BoxMetadata) Version(ver string, providerOpts ...*core.BoxProvider) (v *core.BoxVersion, err error) {
if len(providerOpts) == 0 {
providerOpts = []*core.BoxProvider{
nil,
}
}
for _, p := range providerOpts {
boxVer, err := b.version(ver, p)
if err != nil {
return nil, err
}
if boxVer != nil {
var coreBoxVersion *core.BoxVersion
mapstructure.Decode(boxVer, &coreBoxVersion)
return coreBoxVersion, nil
}
}
return
}
func (b *BoxMetadata) ListVersions(providerOpts ...*core.BoxProvider) ([]string, error) {
v := []string{}
for _, version := range b.Versions {
if providerOpts != nil {
var boxVersionProvider []*BoxVersionProvider
mapstructure.Decode(providerOpts, &boxVersionProvider)
for _, p := range version.Providers {
if p.MatchesAny(boxVersionProvider...) {
v = append(v, version.Version)
}
}
} else {
v = append(v, version.Version)
}
}
return v, nil
}
func (b *BoxMetadata) Provider(version string, name string) (p *core.BoxProvider, err error) {
ver, err := b.version(version, &core.BoxProvider{Name: name})
if err != nil {
return nil, err
}
if ver == nil {
return nil, err
}
provider, err := ver.Provider(name)
if err != nil {
return nil, err
}
if provider != nil {
var coreProvider *core.BoxProvider
mapstructure.Decode(provider, &coreProvider)
var coreVersion *core.BoxVersion
mapstructure.Decode(ver, &coreVersion)
coreProvider.Version = coreVersion
return coreProvider, nil
}
return
}
func (b *BoxMetadata) ListProviders(version string) (providers []string, err error) {
ver, err := b.version(version, &core.BoxProvider{})
if err != nil {
return nil, err
}
return ver.ListProviders()
}
var _ core.BoxMetadata = (*BoxMetadata)(nil)