* Install missing plugins on startup This commit addresses the problem of missing plugins by scanning the snapshot and language host on startup for the list of required plugins and, if there are any plugins that are required but not installed, installs them. The mechanism by which plugins are installed is exactly the same as 'pulumi plugin install'. The installation of missing plugins is best-effort and, if it fails, will not fail the update. This commit addresses pulumi/pulumi-azure#200, where users using Pulumi in CI often found themselves missing plugins. * Add CHANGELOG * Skip downloading plugins if no client provided * Reduce excessive test output * Update Gopkg.lock * Update pkg/engine/destroy.go Co-Authored-By: swgillespie <sean@pulumi.com> * CR: make pluginSet a newtype * CR: Assign loop induction var to local var
115 lines
3.9 KiB
Go
115 lines
3.9 KiB
Go
// Copyright 2016-2018, Pulumi Corporation.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package providers
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/pulumi/pulumi/pkg/resource"
|
|
"github.com/pulumi/pulumi/pkg/resource/plugin"
|
|
"github.com/pulumi/pulumi/pkg/tokens"
|
|
"github.com/pulumi/pulumi/pkg/util/contract"
|
|
)
|
|
|
|
// A provider reference is (URN, ID) tuple that refers to a particular provider instance. A provider reference's
|
|
// string representation is <URN> "::" <ID>. The URN's type portion must be of the form "pulumi:providers:<pkg>".
|
|
|
|
// UnknownID is a distinguished token used to indicate that a provider's ID is not known (e.g. because we are
|
|
// performing a preview).
|
|
const UnknownID = plugin.UnknownStringValue
|
|
|
|
// IsProviderType returns true if the supplied type token refers to a Pulumi provider.
|
|
func IsProviderType(typ tokens.Type) bool {
|
|
// Tokens without a module member are definitely not provider types.
|
|
if !tokens.Token(typ).HasModuleMember() {
|
|
return false
|
|
}
|
|
return typ.Module() == "pulumi:providers" && typ.Name() != ""
|
|
}
|
|
|
|
// MakeProviderType returns the provider type token for the given package.
|
|
func MakeProviderType(pkg tokens.Package) tokens.Type {
|
|
return tokens.Type("pulumi:providers:" + pkg)
|
|
}
|
|
|
|
// GetProviderPackage returns the provider package for the given type token.
|
|
func GetProviderPackage(typ tokens.Type) tokens.Package {
|
|
contract.Require(IsProviderType(typ), "typ")
|
|
return tokens.Package(typ.Name())
|
|
}
|
|
|
|
func validateURN(urn resource.URN) error {
|
|
typ := urn.Type()
|
|
if typ.Module() != "pulumi:providers" {
|
|
return errors.Errorf("invalid module in type: expected 'pulumi:providers', got '%v'", typ.Module())
|
|
}
|
|
if typ.Name() == "" {
|
|
return errors.New("provider URNs must specify a type name")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Reference represents a reference to a particular provider.
|
|
type Reference struct {
|
|
urn resource.URN
|
|
id resource.ID
|
|
}
|
|
|
|
// URN returns the provider reference's URN.
|
|
func (r Reference) URN() resource.URN {
|
|
return r.urn
|
|
}
|
|
|
|
// ID returns the provider reference's ID.
|
|
func (r Reference) ID() resource.ID {
|
|
return r.id
|
|
}
|
|
|
|
// String returns the string representation of this provider reference.
|
|
func (r Reference) String() string {
|
|
return string(r.urn) + resource.URNNameDelimiter + string(r.id)
|
|
}
|
|
|
|
// NewReference creates a new reference for the given URN and ID.
|
|
func NewReference(urn resource.URN, id resource.ID) (Reference, error) {
|
|
if err := validateURN(urn); err != nil {
|
|
return Reference{}, err
|
|
}
|
|
return Reference{urn: urn, id: id}, nil
|
|
}
|
|
|
|
func mustNewReference(urn resource.URN, id resource.ID) Reference {
|
|
ref, err := NewReference(urn, id)
|
|
contract.Assert(err == nil)
|
|
return ref
|
|
}
|
|
|
|
// ParseReference parses the URN and ID from the string representation of a provider reference. If parsing was
|
|
// not possible, this function returns false.
|
|
func ParseReference(s string) (Reference, error) {
|
|
// If this is not a valid URN + ID, return false. Note that we don't try terribly hard to validate the URN portion
|
|
// of the reference.
|
|
lastSep := strings.LastIndex(s, resource.URNNameDelimiter)
|
|
if lastSep == -1 {
|
|
return Reference{}, errors.Errorf("expected '%v' in provider reference '%v'", resource.URNNameDelimiter, s)
|
|
}
|
|
urn, id := resource.URN(s[:lastSep]), resource.ID(s[lastSep+len(resource.URNNameDelimiter):])
|
|
if err := validateURN(urn); err != nil {
|
|
return Reference{}, err
|
|
}
|
|
return Reference{urn: urn, id: id}, nil
|
|
}
|