Skip to content

Commit

Permalink
feat: add harvester_cloudinit_secret
Browse files Browse the repository at this point in the history
Signed-off-by: Frank Yang <[email protected]>
  • Loading branch information
FrankYang0529 committed Apr 27, 2023
1 parent 2444e48 commit c3b9fdb
Show file tree
Hide file tree
Showing 7 changed files with 343 additions and 16 deletions.
30 changes: 30 additions & 0 deletions internal/provider/cloudinitsecret/datasource_cloudinitsecret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package cloudinitsecret

import (
"context"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/harvester/terraform-provider-harvester/pkg/client"
"github.com/harvester/terraform-provider-harvester/pkg/constants"
)

func DataSourceCloudInitSecret() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceCloudInitSecretRead,
Schema: DataSourceSchema(),
}
}

func dataSourceCloudInitSecretRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client.Client)
namespace := d.Get(constants.FieldCommonNamespace).(string)
name := d.Get(constants.FieldCommonName).(string)
secret, err := c.KubeClient.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
return diag.FromErr(err)
}
return diag.FromErr(resourceCloudInitSecretImport(d, secret))
}
131 changes: 131 additions & 0 deletions internal/provider/cloudinitsecret/resource_cloudinitsecret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package cloudinitsecret

import (
"context"
"encoding/base64"
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/harvester/terraform-provider-harvester/internal/util"
"github.com/harvester/terraform-provider-harvester/pkg/client"
"github.com/harvester/terraform-provider-harvester/pkg/constants"
"github.com/harvester/terraform-provider-harvester/pkg/helper"
"github.com/harvester/terraform-provider-harvester/pkg/importer"
)

func ResourceCloudInitSecret() *schema.Resource {
return &schema.Resource{
CreateContext: resourceCloudInitSecretCreate,
ReadContext: resourceCloudInitSecretRead,
DeleteContext: resourceCloudInitSecretDelete,
UpdateContext: resourceCloudInitSecretUpdate,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Schema: Schema(),
}
}

func resourceCloudInitSecretCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client.Client)
namespace := d.Get(constants.FieldCommonNamespace).(string)
name := d.Get(constants.FieldCommonName).(string)
toCreate, err := util.ResourceConstruct(d, Creator(namespace, name))
if err != nil {
return diag.FromErr(err)
}
obj, err := c.KubeClient.CoreV1().Secrets(namespace).Create(ctx, toCreate.(*corev1.Secret), metav1.CreateOptions{})
if err != nil {
return diag.FromErr(err)
}

return diag.FromErr(resourceCloudInitSecretImport(d, obj))
}

func resourceCloudInitSecretUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client.Client)
namespace, name, err := helper.IDParts(d.Id())
if err != nil {
return diag.FromErr(err)
}
obj, err := c.KubeClient.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
d.SetId("")
return nil
}
return diag.FromErr(err)
}
toUpdate, err := util.ResourceConstruct(d, Updater(obj))
if err != nil {
return diag.FromErr(err)
}
_, err = c.KubeClient.CoreV1().Secrets(namespace).Update(ctx, toUpdate.(*corev1.Secret), metav1.UpdateOptions{})
if err != nil {
return diag.FromErr(err)
}

return resourceCloudInitSecretRead(ctx, d, meta)
}

func resourceCloudInitSecretRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client.Client)
namespace, name, err := helper.IDParts(d.Id())
if err != nil {
return diag.FromErr(err)
}
obj, err := c.KubeClient.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
d.SetId("")
return nil
}
return diag.FromErr(err)
}
return diag.FromErr(resourceCloudInitSecretImport(d, obj))
}

func resourceCloudInitSecretDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client.Client)
namespace, name, err := helper.IDParts(d.Id())
if err != nil {
return diag.FromErr(err)
}
err = c.KubeClient.CoreV1().Secrets(namespace).Delete(ctx, name, metav1.DeleteOptions{})
if err != nil && !apierrors.IsNotFound(err) {
return diag.FromErr(err)
}
d.SetId("")
return nil
}

func resourceCloudInitSecretImport(d *schema.ResourceData, obj *corev1.Secret) error {
stateGetter, err := importer.ResourceCloudInitSecretStateGetter(obj)
if err != nil {
return err
}

if d.Get(constants.FieldCloudInitSecretUserData) != "" && d.Get(constants.FieldCloudInitSecretUserDataBase64) == "" {
userdata, err := base64.StdEncoding.DecodeString(stateGetter.States[constants.FieldCloudInitSecretUserDataBase64].(string))
if err != nil {
return fmt.Errorf("can't decode userdata: %v", err)
}
stateGetter.States[constants.FieldCloudInitSecretUserData] = string(userdata)
stateGetter.States[constants.FieldCloudInitSecretUserDataBase64] = ""
}
if d.Get(constants.FieldCloudInitSecretNetworkData) != "" && d.Get(constants.FieldCloudInitSecretNetworkDataBase64) == "" {
networkdata, err := base64.StdEncoding.DecodeString(stateGetter.States[constants.FieldCloudInitSecretNetworkDataBase64].(string))
if err != nil {
return fmt.Errorf("can't decode userdata: %v", err)
}
stateGetter.States[constants.FieldCloudInitSecretUserData] = string(networkdata)
stateGetter.States[constants.FieldCloudInitSecretUserDataBase64] = ""
}

return util.ResourceStatesSet(d, stateGetter)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package cloudinitsecret

import (
"encoding/base64"
"fmt"

corev1 "k8s.io/api/core/v1"

"github.com/harvester/terraform-provider-harvester/internal/util"
"github.com/harvester/terraform-provider-harvester/pkg/constants"
)

var (
_ util.Constructor = &Constructor{}
)

type Constructor struct {
CloudInitSecret *corev1.Secret
}

func (c *Constructor) Setup() util.Processors {
if c.CloudInitSecret.StringData == nil {
c.CloudInitSecret.StringData = map[string]string{}
}
processors := util.NewProcessors().Tags(&c.CloudInitSecret.Labels).Description(&c.CloudInitSecret.Annotations)
customProcessors := []util.Processor{
{
Field: constants.FieldCloudInitSecretUserData,
Parser: func(i interface{}) error {
c.CloudInitSecret.StringData["userdata"] = i.(string)
return nil
},
},
{
Field: constants.FieldCloudInitSecretUserDataBase64,
Parser: func(i interface{}) error {
value, err := base64.StdEncoding.DecodeString(i.(string))
if err != nil {
return fmt.Errorf("failed to decode %s string: %w", constants.FieldCloudInitSecretUserDataBase64, err)
}
c.CloudInitSecret.StringData["userdata"] = string(value)
return nil
},
},
{
Field: constants.FieldCloudInitSecretNetworkData,
Parser: func(i interface{}) error {
c.CloudInitSecret.StringData["networkdata"] = i.(string)
return nil
},
},
{
Field: constants.FieldCloudInitSecretNetworkDataBase64,
Parser: func(i interface{}) error {
value, err := base64.StdEncoding.DecodeString(i.(string))
if err != nil {
return fmt.Errorf("failed to decode %s string: %w", constants.FieldCloudInitSecretNetworkDataBase64, err)
}
c.CloudInitSecret.StringData["networkdata"] = string(value)
return nil
},
},
}
return append(processors, customProcessors...)
}

func (c *Constructor) Validate() error {
return nil
}

func (c *Constructor) Result() (interface{}, error) {
return c.CloudInitSecret, nil
}

func newCloudInitSecretConstructor(cloudInitSecret *corev1.Secret) util.Constructor {
return &Constructor{
CloudInitSecret: cloudInitSecret,
}
}

func Creator(namespace, name string) util.Constructor {
cloudInitSecret := &corev1.Secret{
ObjectMeta: util.NewObjectMeta(namespace, name),
}
return newCloudInitSecretConstructor(cloudInitSecret)
}

func Updater(cloudInitSecret *corev1.Secret) util.Constructor {
return newCloudInitSecretConstructor(cloudInitSecret)
}
35 changes: 35 additions & 0 deletions internal/provider/cloudinitsecret/schema_cloudinitsecret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package cloudinitsecret

import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/harvester/terraform-provider-harvester/internal/util"
"github.com/harvester/terraform-provider-harvester/pkg/constants"
)

func Schema() map[string]*schema.Schema {
s := map[string]*schema.Schema{
constants.FieldCloudInitSecretUserData: {
Type: schema.TypeString,
Optional: true,
},
constants.FieldCloudInitSecretUserDataBase64: {
Type: schema.TypeString,
Optional: true,
},
constants.FieldCloudInitSecretNetworkData: {
Type: schema.TypeString,
Optional: true,
},
constants.FieldCloudInitSecretNetworkDataBase64: {
Type: schema.TypeString,
Optional: true,
},
}
util.NamespacedSchemaWrap(s, false)
return s
}

func DataSourceSchema() map[string]*schema.Schema {
return util.DataSourceSchemaWrap(Schema())
}
35 changes: 19 additions & 16 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/mitchellh/go-homedir"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/harvester/terraform-provider-harvester/internal/provider/cloudinitsecret"
"github.com/harvester/terraform-provider-harvester/internal/provider/clusternetwork"
"github.com/harvester/terraform-provider-harvester/internal/provider/image"
"github.com/harvester/terraform-provider-harvester/internal/provider/keypair"
Expand Down Expand Up @@ -39,24 +40,26 @@ func Provider() *schema.Provider {
},
},
DataSourcesMap: map[string]*schema.Resource{
constants.ResourceTypeImage: image.DataSourceImage(),
constants.ResourceTypeKeyPair: keypair.DataSourceKeypair(),
constants.ResourceTypeNetwork: network.DataSourceNetwork(),
constants.ResourceTypeVirtualMachine: virtualmachine.DataSourceVirtualMachine(),
constants.ResourceTypeVolume: volume.DataSourceVolume(),
constants.ResourceTypeClusterNetwork: clusternetwork.DataSourceClusterNetwork(),
constants.ResourceTypeStorageClass: storageclass.DataSourceStorageClass(),
constants.ResourceTypeVLANConfig: vlanconfig.DataSourceVLANConfig(),
constants.ResourceTypeImage: image.DataSourceImage(),
constants.ResourceTypeKeyPair: keypair.DataSourceKeypair(),
constants.ResourceTypeNetwork: network.DataSourceNetwork(),
constants.ResourceTypeVirtualMachine: virtualmachine.DataSourceVirtualMachine(),
constants.ResourceTypeVolume: volume.DataSourceVolume(),
constants.ResourceTypeClusterNetwork: clusternetwork.DataSourceClusterNetwork(),
constants.ResourceTypeStorageClass: storageclass.DataSourceStorageClass(),
constants.ResourceTypeVLANConfig: vlanconfig.DataSourceVLANConfig(),
constants.ResourceTypeCloudInitSecret: cloudinitsecret.DataSourceCloudInitSecret(),
},
ResourcesMap: map[string]*schema.Resource{
constants.ResourceTypeImage: image.ResourceImage(),
constants.ResourceTypeKeyPair: keypair.ResourceKeypair(),
constants.ResourceTypeNetwork: network.ResourceNetwork(),
constants.ResourceTypeVirtualMachine: virtualmachine.ResourceVirtualMachine(),
constants.ResourceTypeVolume: volume.ResourceVolume(),
constants.ResourceTypeClusterNetwork: clusternetwork.ResourceClusterNetwork(),
constants.ResourceTypeStorageClass: storageclass.ResourceStorageClass(),
constants.ResourceTypeVLANConfig: vlanconfig.ResourceVLANConfig(),
constants.ResourceTypeImage: image.ResourceImage(),
constants.ResourceTypeKeyPair: keypair.ResourceKeypair(),
constants.ResourceTypeNetwork: network.ResourceNetwork(),
constants.ResourceTypeVirtualMachine: virtualmachine.ResourceVirtualMachine(),
constants.ResourceTypeVolume: volume.ResourceVolume(),
constants.ResourceTypeClusterNetwork: clusternetwork.ResourceClusterNetwork(),
constants.ResourceTypeStorageClass: storageclass.ResourceStorageClass(),
constants.ResourceTypeVLANConfig: vlanconfig.ResourceVLANConfig(),
constants.ResourceTypeCloudInitSecret: cloudinitsecret.ResourceCloudInitSecret(),
},
}
p.ConfigureContextFunc = configure(p)
Expand Down
10 changes: 10 additions & 0 deletions pkg/constants/constants_cloudinitsecret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package constants

const (
ResourceTypeCloudInitSecret = "harvester_cloudinit_secret"

FieldCloudInitSecretUserData = "user_data"
FieldCloudInitSecretNetworkData = "network_data"
FieldCloudInitSecretUserDataBase64 = "user_data_base64" // #nosec G101
FieldCloudInitSecretNetworkDataBase64 = "network_data_base64"
)
28 changes: 28 additions & 0 deletions pkg/importer/resource_cloudinitsecret_importer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package importer

import (
"encoding/base64"

corev1 "k8s.io/api/core/v1"

"github.com/harvester/terraform-provider-harvester/pkg/constants"
"github.com/harvester/terraform-provider-harvester/pkg/helper"
)

func ResourceCloudInitSecretStateGetter(obj *corev1.Secret) (*StateGetter, error) {
states := map[string]interface{}{
constants.FieldCommonNamespace: obj.Namespace,
constants.FieldCommonName: obj.Name,
constants.FieldCommonDescription: GetDescriptions(obj.Annotations),
constants.FieldCommonTags: GetTags(obj.Labels),
constants.FieldCloudInitSecretUserDataBase64: base64.StdEncoding.EncodeToString(obj.Data["userdata"]),
constants.FieldCloudInitSecretNetworkDataBase64: base64.StdEncoding.EncodeToString(obj.Data["networkdata"]),
}

return &StateGetter{
ID: helper.BuildID(obj.Namespace, obj.Name),
Name: obj.Name,
ResourceType: constants.ResourceTypeCloudInitSecret,
States: states,
}, nil
}

0 comments on commit c3b9fdb

Please sign in to comment.