Files
atlantis/server/core/terraform/distribution.go
2025-01-02 21:06:01 -08:00

142 lines
3.4 KiB
Go

package terraform
import (
"context"
"fmt"
"sort"
"github.com/hashicorp/go-version"
"github.com/hashicorp/hc-install/product"
"github.com/hashicorp/hc-install/releases"
"github.com/opentofu/tofudl"
)
type Distribution interface {
BinName() string
Downloader() Downloader
// ResolveConstraint gets the latest version for the given constraint
ResolveConstraint(context.Context, string) (*version.Version, error)
}
func NewDistribution(distribution string) Distribution {
tfDistribution := NewDistributionTerraform()
if distribution == "opentofu" {
tfDistribution = NewDistributionOpenTofu()
}
return tfDistribution
}
type DistributionOpenTofu struct {
downloader Downloader
}
func NewDistributionOpenTofu() Distribution {
return &DistributionOpenTofu{
downloader: &TofuDownloader{},
}
}
func NewDistributionOpenTofuWithDownloader(downloader Downloader) Distribution {
return &DistributionOpenTofu{
downloader: downloader,
}
}
func (*DistributionOpenTofu) BinName() string {
return "tofu"
}
func (d *DistributionOpenTofu) Downloader() Downloader {
return d.downloader
}
func (*DistributionOpenTofu) ResolveConstraint(ctx context.Context, constraintStr string) (*version.Version, error) {
dl, err := tofudl.New()
if err != nil {
return nil, err
}
vc, err := version.NewConstraint(constraintStr)
if err != nil {
return nil, fmt.Errorf("error parsing constraint string: %s", err)
}
allVersions, err := dl.ListVersions(ctx)
if err != nil {
return nil, fmt.Errorf("error listing OpenTofu versions: %s", err)
}
var versions []*version.Version
for _, ver := range allVersions {
v, err := version.NewVersion(string(ver.ID))
if err != nil {
return nil, err
}
if vc.Check(v) {
versions = append(versions, v)
}
}
sort.Sort(version.Collection(versions))
if len(versions) == 0 {
return nil, fmt.Errorf("no OpenTofu versions found for constraints %s", constraintStr)
}
// We want to select the highest version that satisfies the constraint.
version := versions[len(versions)-1]
// Get the Version object from the versionDownloader.
return version, nil
}
type DistributionTerraform struct {
downloader Downloader
}
func NewDistributionTerraform() Distribution {
return &DistributionTerraform{
downloader: &TerraformDownloader{},
}
}
func NewDistributionTerraformWithDownloader(downloader Downloader) Distribution {
return &DistributionTerraform{
downloader: downloader,
}
}
func (*DistributionTerraform) BinName() string {
return "terraform"
}
func (d *DistributionTerraform) Downloader() Downloader {
return d.downloader
}
func (*DistributionTerraform) ResolveConstraint(ctx context.Context, constraintStr string) (*version.Version, error) {
vc, err := version.NewConstraint(constraintStr)
if err != nil {
return nil, fmt.Errorf("error parsing constraint string: %s", err)
}
constrainedVersions := &releases.Versions{
Product: product.Terraform,
Constraints: vc,
}
installCandidates, err := constrainedVersions.List(ctx)
if err != nil {
return nil, fmt.Errorf("error listing available versions: %s", err)
}
if len(installCandidates) == 0 {
return nil, fmt.Errorf("no Terraform versions found for constraints %s", constraintStr)
}
// We want to select the highest version that satisfies the constraint.
versionDownloader := installCandidates[len(installCandidates)-1]
// Get the Version object from the versionDownloader.
return versionDownloader.(*releases.ExactVersion).Version, nil
}