package config import "github.com/blang/semver" // ProviderVersionConstraint presents a constraint for a particular // provider, identified by its full name. type ProviderVersionConstraint struct { Constraint string ProviderType string } // ProviderVersionConstraints is a map from provider full name to its associated // ProviderVersionConstraint, as produced by Config.RequiredProviders. type ProviderVersionConstraints map[string]ProviderVersionConstraint // RequiredProviders returns the ProviderVersionConstraints for this // module. // // This includes both providers that are explicitly requested by provider // blocks and those that are used implicitly by instantiating one of their // resource types. In the latter case, the returned semver Range will // accept any version of the provider. func (c *Config) RequiredProviders() ProviderVersionConstraints { ret := make(ProviderVersionConstraints, len(c.ProviderConfigs)) configs := c.ProviderConfigsByFullName() // In order to find the *implied* dependencies (those without explicit // "provider" blocks) we need to walk over all of the resources and // cross-reference with the provider configs. for _, rc := range c.Resources { providerName := rc.ProviderFullName() var providerType string // Default to (effectively) no constraint whatsoever, but we might // override if there's an explicit constraint in config. constraint := ">=0.0.0" config, ok := configs[providerName] if ok { if config.Version != "" { constraint = config.Version } providerType = config.Name } else { providerType = providerName } ret[providerName] = ProviderVersionConstraint{ ProviderType: providerType, Constraint: constraint, } } return ret } // RequiredRanges returns a semver.Range for each distinct provider type in // the constraint map. If the same provider type appears more than once // (e.g. because aliases are in use) then their respective constraints are // combined such that they must *all* apply. // // The result of this method can be passed to the // PluginMetaSet.ConstrainVersions method within the plugin/discovery // package in order to filter down the available plugins to those which // satisfy the given constraints. // // This function will panic if any of the constraints within cannot be // parsed as semver ranges. This is guaranteed to never happen for a // constraint set that was built from a configuration that passed validation. func (cons ProviderVersionConstraints) RequiredRanges() map[string]semver.Range { ret := make(map[string]semver.Range, len(cons)) for _, con := range cons { spec := semver.MustParseRange(con.Constraint) if existing, exists := ret[con.ProviderType]; exists { ret[con.ProviderType] = existing.AND(spec) } else { ret[con.ProviderType] = spec } } return ret } // ProviderConfigsByFullName returns a map from provider full names (as // returned by ProviderConfig.FullName()) to the corresponding provider // configs. // // This function returns no new information than what's already in // c.ProviderConfigs, but returns it in a more convenient shape. If there // is more than one provider config with the same full name then the result // is undefined, but that is guaranteed not to happen for any config that // has passed validation. func (c *Config) ProviderConfigsByFullName() map[string]*ProviderConfig { ret := make(map[string]*ProviderConfig, len(c.ProviderConfigs)) for _, pc := range c.ProviderConfigs { ret[pc.FullName()] = pc } return ret }