backport of commit 94028656de

pull/12537/head
Wilken Rivera 3 years ago
parent d307b2916e
commit 6a7ec16da5

@ -7,6 +7,7 @@ package api
import (
"fmt"
"log"
"net/http"
"os"
"time"
@ -44,39 +45,67 @@ func NewClient() (*Client, error) {
}
}
cl, err := httpclient.New(httpclient.Config{
hcpClientCfg := httpclient.Config{
SourceChannel: fmt.Sprintf("packer/%s", version.PackerVersion.FormattedVersion()),
})
if err != nil {
}
if err := hcpClientCfg.Canonicalize(); err != nil {
return nil, &ClientError{
StatusCode: InvalidClientConfig,
Err: err,
}
}
cl, err := httpclient.New(hcpClientCfg)
if err != nil {
return nil, &ClientError{
StatusCode: InvalidClientConfig,
Err: err,
}
}
client := &Client{
Packer: packerSvc.New(cl, nil),
Organization: organizationSvc.New(cl, nil),
Project: projectSvc.New(cl, nil),
}
//if both HCP_ORGANIZATION_ID and HCP_PROJECT_ID are set via env variables the hcpConfig may have all we need already.
if hcpClientCfg.Profile().OrganizationID != "" && hcpClientCfg.Profile().ProjectID != "" {
client.OrganizationID = hcpClientCfg.Profile().OrganizationID
client.ProjectID = hcpClientCfg.Profile().ProjectID
if err := client.loadOrganizationID(); err != nil {
return nil, &ClientError{
StatusCode: InvalidClientConfig,
Err: err,
}
return client, nil
}
if err := client.loadProjectID(); err != nil {
return nil, &ClientError{
StatusCode: InvalidClientConfig,
Err: err,
if client.OrganizationID == "" {
err := client.loadOrganizationID()
if err != nil {
return nil, &ClientError{
StatusCode: InvalidClientConfig,
Err: err,
}
}
}
if client.ProjectID == "" {
err := client.loadProjectID()
if err != nil {
return nil, &ClientError{
StatusCode: InvalidClientConfig,
Err: err,
}
}
}
return client, nil
}
func (c *Client) loadOrganizationID() error {
if c.OrganizationID != "" {
return nil
}
if env.HasOrganizationID() {
c.OrganizationID = os.Getenv(env.HCPOrganizationID)
return nil
}
// Get the organization ID.
listOrgParams := organizationSvc.NewOrganizationServiceListParams()
listOrgResp, err := c.Organization.OrganizationServiceList(listOrgParams, nil)
@ -92,55 +121,62 @@ func (c *Client) loadOrganizationID() error {
}
func (c *Client) loadProjectID() error {
if c.ProjectID != "" {
return nil
}
if env.HasProjectID() {
c.ProjectID = os.Getenv(env.HCPProjectID)
return nil
}
// Get the project using the organization ID.
listProjParams := projectSvc.NewProjectServiceListParams()
listProjParams.ScopeID = &c.OrganizationID
scopeType := string(rmmodels.HashicorpCloudResourcemanagerResourceIDResourceTypeORGANIZATION)
listProjParams.ScopeType = &scopeType
listProjResp, err := c.Project.ProjectServiceList(listProjParams, nil)
if err != nil {
return fmt.Errorf("unable to fetch project id: %v", err)
}
if env.HasProjectID() {
proj, err := findProjectByID(os.Getenv(env.HCPProjectID), listProjResp.Payload.Projects)
if err != nil {
return err
}
c.ProjectID = proj.ID
} else {
if len(listProjResp.Payload.Projects) > 1 {
log.Printf("[WARNING] Multiple HCP projects found, will pick the oldest one by default\n" +
"To specify which project to use, set the HCP_PROJECT_ID environment variable to the one you want to use.")
if err != nil {
//For permission errors our service principle may not have the perms
// to see all projects for an Org; this is the case for project-level service principles.
serviceErr, ok := err.(*projectSvc.ProjectServiceListDefault)
if !ok {
return fmt.Errorf("unable to fetch project list: %v", err)
}
proj, err := findOldestProject(listProjResp.Payload.Projects)
if err != nil {
return err
if serviceErr.Code() == http.StatusForbidden {
return fmt.Errorf("unable to fetch project\n\n"+
"If the provided credentials are tied to a specific project trying setting the %s environment variable to one you want to use.", env.HCPProjectID)
}
}
c.ProjectID = proj.ID
if len(listProjResp.Payload.Projects) > 1 {
log.Printf("[WARNING] Multiple HCP projects found, will pick the oldest one by default\n"+
"To specify which project to use, set the %s environment variable to the one you want to use.", env.HCPProjectID)
}
proj, err := getOldestProject(listProjResp.Payload.Projects)
if err != nil {
return err
}
c.ProjectID = proj.ID
return nil
}
func findOldestProject(projs []*models.HashicorpCloudResourcemanagerProject) (*models.HashicorpCloudResourcemanagerProject, error) {
if len(projs) == 0 {
// getOldestProject retrieves the oldest project from a list based on its created_at time.
func getOldestProject(projects []*models.HashicorpCloudResourcemanagerProject) (*models.HashicorpCloudResourcemanagerProject, error) {
if len(projects) == 0 {
return nil, fmt.Errorf("no project found")
}
proj := projs[0]
for i := 1; i < len(projs); i++ {
nxtProj := projs[i]
if time.Time(nxtProj.CreatedAt).Before(time.Time(proj.CreatedAt)) {
proj = nxtProj
oldestTime := time.Now()
var oldestProj *models.HashicorpCloudResourcemanagerProject
for _, proj := range projects {
projTime := time.Time(proj.CreatedAt)
if projTime.Before(oldestTime) {
oldestProj = proj
oldestTime = projTime
}
}
return proj, nil
return oldestProj, nil
}
func findProjectByID(projID string, projs []*models.HashicorpCloudResourcemanagerProject) (*models.HashicorpCloudResourcemanagerProject, error) {

@ -91,7 +91,7 @@ func TestFindProjectID(t *testing.T) {
}
}
func TestFindOldestProject(t *testing.T) {
func TestGetOldestProject(t *testing.T) {
testcases := []struct {
Name string
ProjectList []*models.HashicorpCloudResourcemanagerProject
@ -148,7 +148,7 @@ func TestFindOldestProject(t *testing.T) {
for _, tt := range testcases {
t.Run(tt.Name, func(t *testing.T) {
proj, err := findOldestProject(tt.ProjectList)
proj, err := getOldestProject(tt.ProjectList)
if (err != nil) != tt.ExpectErr {
t.Errorf("test findProjectByID, expected %t, got %t",
tt.ExpectErr,

@ -13,6 +13,10 @@ func HasProjectID() bool {
return hasEnvVar(HCPProjectID)
}
func HasOrganizationID() bool {
return hasEnvVar(HCPOrganizationID)
}
func HasClientID() bool {
return hasEnvVar(HCPClientID)
}

@ -7,6 +7,7 @@ const (
HCPClientID = "HCP_CLIENT_ID"
HCPClientSecret = "HCP_CLIENT_SECRET"
HCPProjectID = "HCP_PROJECT_ID"
HCPOrganizationID = "HCP_ORGANIZATION_ID"
HCPPackerRegistry = "HCP_PACKER_REGISTRY"
HCPPackerBucket = "HCP_PACKER_BUCKET_NAME"
HCPPackerBuildFingerprint = "HCP_PACKER_BUILD_FINGERPRINT"

Loading…
Cancel
Save