Skip to content

Commit

Permalink
Add support for LINODE_CA env var and api_ca_path attribute (#270)
Browse files Browse the repository at this point in the history
* Add support for LINODE_CA env var and api_ca_path attribute

* Making client a pointer; checking errors
  • Loading branch information
zliang-akamai authored Nov 4, 2024
1 parent e432640 commit 4343f20
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 12 deletions.
3 changes: 3 additions & 0 deletions .web-docs/components/builder/linode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ can also be supplied to override the typical auto-generated key:
`images:read_write`, `linodes:read_write`, and `events:read_only`
scopes are required for the API token.

- `api_ca_path` (string) - The path to a CA file to trust when making API requests.
It can also be specified using the `LINODE_CA` environment variable.

<!-- End of code generated from the comments of the LinodeCommon struct in helper/common.go; -->

<!-- Code generated from the comments of the Config struct in builder/linode/config.go; DO NOT EDIT MANUALLY -->
Expand Down
3 changes: 3 additions & 0 deletions .web-docs/components/data-source/image/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ data "linode-image" "ubuntu22_lts" {
`images:read_write`, `linodes:read_write`, and `events:read_only`
scopes are required for the API token.

- `api_ca_path` (string) - The path to a CA file to trust when making API requests.
It can also be specified using the `LINODE_CA` environment variable.

<!-- End of code generated from the comments of the LinodeCommon struct in helper/common.go; -->


Expand Down
12 changes: 10 additions & 2 deletions builder/linode/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,16 @@ func (b *Builder) Prepare(raws ...any) ([]string, []string, error) {

func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (ret packersdk.Artifact, err error) {
ui.Say("Running builder ...")
var client *linodego.Client

client := helper.NewLinodeClient(b.config.PersonalAccessToken)
if b.config.APICAPath != "" {
client, err = helper.NewLinodeClientWithCA(b.config.PersonalAccessToken, b.config.APICAPath)
if err != nil {
return nil, err
}
} else {
client = helper.NewLinodeClient(b.config.PersonalAccessToken)
}

state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
Expand Down Expand Up @@ -90,7 +98,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
artifact := Artifact{
ImageLabel: image.Label,
ImageID: image.ID,
Driver: &client,
Driver: client,
StateData: map[string]any{
"generated_data": state.Get("generated_data"),
"source_image": b.config.Image,
Expand Down
4 changes: 4 additions & 0 deletions builder/linode/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ func (c *Config) Prepare(raws ...any) ([]string, error) {
c.PersonalAccessToken = os.Getenv("LINODE_TOKEN")
}

if c.APICAPath == "" {
c.APICAPath = os.Getenv("LINODE_CA")
}

if c.ImageLabel == "" {
if def, err := interpolate.Render("packer-{{timestamp}}", nil); err == nil {
c.ImageLabel = def
Expand Down
2 changes: 2 additions & 0 deletions builder/linode/config.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion builder/linode/step_create_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

type stepCreateImage struct {
client linodego.Client
client *linodego.Client
}

func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
Expand Down
2 changes: 1 addition & 1 deletion builder/linode/step_create_linode.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

type stepCreateLinode struct {
client linodego.Client
client *linodego.Client
}

func flattenConfigInterfaceIPv4(i *InterfaceIPv4) *linodego.VPCIPv4 {
Expand Down
2 changes: 1 addition & 1 deletion builder/linode/step_shutdown_linode.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

type stepShutdownLinode struct {
client linodego.Client
client *linodego.Client
}

func (s *stepShutdownLinode) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
Expand Down
12 changes: 11 additions & 1 deletion datasource/image/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,17 @@ func (d *Datasource) OutputSpec() hcldec.ObjectSpec {
}

func (d *Datasource) Execute() (cty.Value, error) {
client := helper.NewLinodeClient(d.config.PersonalAccessToken)
var client *linodego.Client
var err error

if d.config.APICAPath != "" {
client, err = helper.NewLinodeClientWithCA(d.config.PersonalAccessToken, d.config.APICAPath)
if err != nil {
return cty.NullVal(cty.EmptyObject), err
}
} else {
client = helper.NewLinodeClient(d.config.PersonalAccessToken)
}

filters := linodego.Filter{}

Expand Down
2 changes: 2 additions & 0 deletions datasource/image/data.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 55 additions & 6 deletions helper/client.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package helper

import (
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
"os"
"path/filepath"

"github.com/linode/linodego"
"github.com/linode/packer-plugin-linode/version"
Expand All @@ -11,14 +15,31 @@ import (

const TokenEnvVar = "LINODE_TOKEN"

func NewLinodeClient(token string) linodego.Client {
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
// AddRootCAToTransport applies the CA at the given path to the given *http.Transport
func AddRootCAToTransport(CAPath string, transport *http.Transport) error {
CAData, err := os.ReadFile(filepath.Clean(CAPath))
if err != nil {
return fmt.Errorf("failed to read CA file %s: %w", CAPath, err)
}

oauthTransport := &oauth2.Transport{
Source: tokenSource,
if transport.TLSClientConfig == nil {
transport.TLSClientConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
}
}

if transport.TLSClientConfig.RootCAs == nil {
transport.TLSClientConfig.RootCAs = x509.NewCertPool()
}

transport.TLSClientConfig.RootCAs.AppendCertsFromPEM(CAData)

return nil
}

func linodeClientFromTransport(transport http.RoundTripper) *linodego.Client {
oauth2Client := &http.Client{
Transport: oauthTransport,
Transport: transport,
}

client := linodego.NewClient(oauth2Client)
Expand All @@ -28,5 +49,33 @@ func NewLinodeClient(token string) linodego.Client {
version.PluginVersion.FormattedVersion(), projectURL, linodego.Version)

client.SetUserAgent(userAgent)
return client
return &client
}

func getDefaultTransportWithCA(CAPath string) (*http.Transport, error) {
httpTransport := http.DefaultTransport.(*http.Transport).Clone()
return httpTransport, AddRootCAToTransport(CAPath, httpTransport)
}

func getOauth2TransportWithToken(token string, baseTransport http.RoundTripper) *oauth2.Transport {
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
oauthTransport := &oauth2.Transport{
Source: tokenSource,
Base: baseTransport,
}
return oauthTransport
}

func NewLinodeClient(token string) *linodego.Client {
oauthTransport := getOauth2TransportWithToken(token, nil)
return linodeClientFromTransport(oauthTransport)
}

func NewLinodeClientWithCA(token, CAPath string) (*linodego.Client, error) {
transport, err := getDefaultTransportWithCA(CAPath)
if err != nil {
return nil, err
}
oauthTransport := getOauth2TransportWithToken(token, transport)
return linodeClientFromTransport(oauthTransport), nil
}
4 changes: 4 additions & 0 deletions helper/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ type LinodeCommon struct {
// `images:read_write`, `linodes:read_write`, and `events:read_only`
// scopes are required for the API token.
PersonalAccessToken string `mapstructure:"linode_token"`

// The path to a CA file to trust when making API requests.
// It can also be specified using the `LINODE_CA` environment variable.
APICAPath string `mapstructure:"api_ca_path"`
}

0 comments on commit 4343f20

Please sign in to comment.