The source parsing logic was heavily directed towards Github compatible
source URIs, however if we want to support more cases, we need to make
sure we are able to specify those URIs, and to load plugins installed
from those sources.
Right now, since the getters available are only github.com, we will not
support remotely instlling plugins from sources other than github.com,
with the same set of constraints as before. However, we do support now
installing from a local plugin binary to any kind of source, and we
support loading them, including if a template wants this plugin
installed locally with version constraints.
// split the source string into individual components
parts:=strings.Split(str,"/")
iflen(parts)!=3{
diags=diags.Append(&hcl.Diagnostic{
Severity:hcl.DiagError,
Summary:"Invalid plugin source string",
Detail:`The "source" attribute must be in the format "hostname/namespace/name"`,
})
returnnil,diags
varerrs[]string
ifstrings.HasPrefix(str,"/"){
errs=append(errs,"A source URL must not start with a '/' character.")
}
// check for an invalid empty string in any part
fori:=rangeparts{
ifparts[i]==""{
diags=diags.Append(&hcl.Diagnostic{
Severity:hcl.DiagError,
Summary:"Invalid plugin source string",
Detail:`The "source" attribute must be in the format "hostname/namespace/name"`,
})
returnnil,diags
}
ifstrings.HasSuffix(str,"/"){
errs=append(errs,"A source URL must not end with a '/' character.")
}
// check the 'name' portion, which is always the last part
givenName:=parts[len(parts)-1]
name,err:=ParsePluginPart(givenName)
if!strings.Contains(str,"/"){
errs=append(errs,"A source URL must at least contain a host and a path.")
}
url,err:=url.Parse(str)
iferr!=nil{
diags=diags.Append(&hcl.Diagnostic{
errs=append(errs,fmt.Sprintf("Failed to parse source URL: %s",err))
}
ifurl!=nil&&url.Scheme!=""{
errs=append(errs,"A source URL must not contain a scheme (e.g. https://).")
}
ifurl!=nil&&url.RawQuery!=""{
errs=append(errs,"A source URL must not contain a query (e.g. ?var=val)")
}
ifurl!=nil&&url.Fragment!=""{
errs=append(errs,"A source URL must not contain a fragment (e.g. #anchor).")
}
iferrs!=nil{
errsMsg:=&strings.Builder{}
for_,err:=rangeerrs{
fmt.Fprintf(errsMsg,"* %s\n",err)
}
returnnil,diags.Append(&hcl.Diagnostic{
Severity:hcl.DiagError,
Summary:"Invalid plugin type",
Detail:fmt.Sprintf(`Invalid plugin type %q in source %q: %s"`,givenName,str,err),
Summary:"Malformed source URL",
Detail:fmt.Sprintf("The provided source URL %q is invalid. The following errors have been discovered:\n%s\nA valid source looks like \"github.com/hashicorp/happycloud\"",str,errsMsg),
})
returnnil,diags
}
ret.Type=name
// the namespace is always the second-to-last part
givenNamespace:=parts[len(parts)-2]
namespace,err:=ParsePluginPart(givenNamespace)
// check the 'name' portion, which is always the last part
_,givenName:=path.Split(str)
_,err= ParsePluginPart(givenName)
iferr!=nil{
diags=diags.Append(&hcl.Diagnostic{
Severity:hcl.DiagError,
Summary:"Invalid plugin namespace",
Detail:fmt.Sprintf(`Invalid plugin namespace %q in source %q: %s"`,namespace,str,err),
Summary:"Invalid plugin type",
Detail:fmt.Sprintf(`Invalid plugin type %q in source %q: %s"`,givenName,str,err),
})
returnnil,diags
}
ret.Namespace=namespace
// the hostname is always the first part in a three-part source string
ret.Hostname=parts[0]
// Due to how plugin executables are named and plugin git repositories
// are conventionally named, it's a reasonable and
Detail:fmt.Sprintf("Plugin source %q has a type with the prefix %q, which isn't valid. "+
"Although that prefix is often used in the names of version control repositories for Packer plugins, "+
"plugin source strings should not include it.\n"+
"\nDid you mean %q?",ret,userErrorPrefix,suggestedAddr),
"\nDid you mean %q?",str,userErrorPrefix,suggestedType),
})
returnnil,diags
}
}
// Otherwise, probably instead an incorrectly-named plugin, perhaps
// arising from a similar instinct to what causes there to be
// thousands of Python packages on PyPI with "python-"-prefixed
// names.
diags=diags.Append(&hcl.Diagnostic{
returnnil,diags.Append(&hcl.Diagnostic{
Severity:hcl.DiagError,
Summary:"Invalid plugin type",
Detail:fmt.Sprintf("Plugin source %q has a type with the prefix %q, which isn't allowed because it would be redundant to name a Packer plugin with that prefix. If you are the author of this plugin, rename it to not include the prefix.",ret,redundantPrefix),
Detail:fmt.Sprintf("Plugin source %q has a type with the prefix %q, which isn't allowed "+
"because it would be redundant to name a Packer plugin with that prefix. "+
"If you are the author of this plugin, rename it to not include the prefix.",