mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
terraform-provider: Add support for STACKIT / OpenStack
This commit is contained in:
parent
1670d977c6
commit
d69673fab7
@ -844,7 +844,7 @@ type applier interface {
|
|||||||
// methods required to install/upgrade Helm charts
|
// methods required to install/upgrade Helm charts
|
||||||
|
|
||||||
PrepareHelmCharts(
|
PrepareHelmCharts(
|
||||||
flags helm.Options, state *state.State, serviceAccURI string, masterSecret uri.MasterSecret, openStackCfg *config.OpenStackConfig,
|
flags helm.Options, state *state.State, serviceAccURI string, masterSecret uri.MasterSecret,
|
||||||
) (helm.Applier, bool, error)
|
) (helm.Applier, bool, error)
|
||||||
|
|
||||||
// methods to interact with Kubernetes
|
// methods to interact with Kubernetes
|
||||||
|
@ -554,7 +554,7 @@ func (s *stubConstellApplier) Init(context.Context, atls.Validator, *state.State
|
|||||||
|
|
||||||
type helmApplier interface {
|
type helmApplier interface {
|
||||||
PrepareHelmCharts(
|
PrepareHelmCharts(
|
||||||
flags helm.Options, stateFile *state.State, serviceAccURI string, masterSecret uri.MasterSecret, openStackCfg *config.OpenStackConfig,
|
flags helm.Options, stateFile *state.State, serviceAccURI string, masterSecret uri.MasterSecret,
|
||||||
) (
|
) (
|
||||||
helm.Applier, bool, error)
|
helm.Applier, bool, error)
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,18 @@ func (a *applyCmd) runHelmApply(cmd *cobra.Command, conf *config.Config, stateFi
|
|||||||
ApplyTimeout: a.flags.helmTimeout,
|
ApplyTimeout: a.flags.helmTimeout,
|
||||||
AllowDestructive: helm.DenyDestructive,
|
AllowDestructive: helm.DenyDestructive,
|
||||||
}
|
}
|
||||||
|
if conf.Provider.OpenStack != nil {
|
||||||
|
var deployYawolLoadBalancer bool
|
||||||
|
if conf.Provider.OpenStack.DeployYawolLoadBalancer != nil {
|
||||||
|
deployYawolLoadBalancer = *conf.Provider.OpenStack.DeployYawolLoadBalancer
|
||||||
|
}
|
||||||
|
options.OpenStackValues = &helm.OpenStackValues{
|
||||||
|
DeployYawolLoadBalancer: deployYawolLoadBalancer,
|
||||||
|
FloatingIPPoolID: conf.Provider.OpenStack.FloatingIPPoolID,
|
||||||
|
YawolFlavorID: conf.Provider.OpenStack.YawolFlavorID,
|
||||||
|
YawolImageID: conf.Provider.OpenStack.YawolImageID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
a.log.Debug("Getting service account URI")
|
a.log.Debug("Getting service account URI")
|
||||||
serviceAccURI, err := cloudcmd.GetMarshaledServiceAccountURI(conf, a.fileHandler)
|
serviceAccURI, err := cloudcmd.GetMarshaledServiceAccountURI(conf, a.fileHandler)
|
||||||
@ -51,7 +63,7 @@ func (a *applyCmd) runHelmApply(cmd *cobra.Command, conf *config.Config, stateFi
|
|||||||
}
|
}
|
||||||
|
|
||||||
a.log.Debug("Preparing Helm charts")
|
a.log.Debug("Preparing Helm charts")
|
||||||
executor, includesUpgrades, err := a.applier.PrepareHelmCharts(options, stateFile, serviceAccURI, masterSecret, conf.Provider.OpenStack)
|
executor, includesUpgrades, err := a.applier.PrepareHelmCharts(options, stateFile, serviceAccURI, masterSecret)
|
||||||
if errors.Is(err, helm.ErrConfirmationMissing) {
|
if errors.Is(err, helm.ErrConfirmationMissing) {
|
||||||
if !a.flags.yes {
|
if !a.flags.yes {
|
||||||
cmd.PrintErrln("WARNING: Upgrading cert-manager will destroy all custom resources you have manually created that are based on the current version of cert-manager.")
|
cmd.PrintErrln("WARNING: Upgrading cert-manager will destroy all custom resources you have manually created that are based on the current version of cert-manager.")
|
||||||
@ -65,7 +77,7 @@ func (a *applyCmd) runHelmApply(cmd *cobra.Command, conf *config.Config, stateFi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
options.AllowDestructive = helm.AllowDestructive
|
options.AllowDestructive = helm.AllowDestructive
|
||||||
executor, includesUpgrades, err = a.applier.PrepareHelmCharts(options, stateFile, serviceAccURI, masterSecret, conf.Provider.OpenStack)
|
executor, includesUpgrades, err = a.applier.PrepareHelmCharts(options, stateFile, serviceAccURI, masterSecret)
|
||||||
}
|
}
|
||||||
var upgradeErr *compatibility.InvalidUpgradeError
|
var upgradeErr *compatibility.InvalidUpgradeError
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -279,7 +279,7 @@ type stubHelmApplier struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s stubHelmApplier) PrepareHelmCharts(
|
func (s stubHelmApplier) PrepareHelmCharts(
|
||||||
_ helm.Options, _ *state.State, _ string, _ uri.MasterSecret, _ *config.OpenStackConfig,
|
_ helm.Options, _ *state.State, _ string, _ uri.MasterSecret,
|
||||||
) (helm.Applier, bool, error) {
|
) (helm.Applier, bool, error) {
|
||||||
return stubRunner{}, false, s.err
|
return stubRunner{}, false, s.err
|
||||||
}
|
}
|
||||||
|
@ -376,9 +376,9 @@ type mockApplier struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockApplier) PrepareHelmCharts(
|
func (m *mockApplier) PrepareHelmCharts(
|
||||||
helmOpts helm.Options, stateFile *state.State, str string, masterSecret uri.MasterSecret, openStackCfg *config.OpenStackConfig,
|
helmOpts helm.Options, stateFile *state.State, str string, masterSecret uri.MasterSecret,
|
||||||
) (helm.Applier, bool, error) {
|
) (helm.Applier, bool, error) {
|
||||||
args := m.Called(helmOpts, stateFile, helmOpts, str, masterSecret, openStackCfg)
|
args := m.Called(helmOpts, stateFile, helmOpts, str, masterSecret)
|
||||||
return args.Get(0).(helm.Applier), args.Bool(1), args.Error(2)
|
return args.Get(0).(helm.Applier), args.Bool(1), args.Error(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ package constellation
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constellation/helm"
|
"github.com/edgelesssys/constellation/v2/internal/constellation/helm"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constellation/state"
|
"github.com/edgelesssys/constellation/v2/internal/constellation/state"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
|
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
|
||||||
@ -17,18 +16,18 @@ import (
|
|||||||
|
|
||||||
// PrepareHelmCharts loads Helm charts for Constellation and returns an executor to apply them.
|
// PrepareHelmCharts loads Helm charts for Constellation and returns an executor to apply them.
|
||||||
func (a *Applier) PrepareHelmCharts(
|
func (a *Applier) PrepareHelmCharts(
|
||||||
flags helm.Options, state *state.State, serviceAccURI string, masterSecret uri.MasterSecret, openStackCfg *config.OpenStackConfig,
|
flags helm.Options, state *state.State, serviceAccURI string, masterSecret uri.MasterSecret,
|
||||||
) (helm.Applier, bool, error) {
|
) (helm.Applier, bool, error) {
|
||||||
if a.helmClient == nil {
|
if a.helmClient == nil {
|
||||||
return nil, false, errors.New("helm client not initialized")
|
return nil, false, errors.New("helm client not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.helmClient.PrepareApply(flags, state, serviceAccURI, masterSecret, openStackCfg)
|
return a.helmClient.PrepareApply(flags, state, serviceAccURI, masterSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
type helmApplier interface {
|
type helmApplier interface {
|
||||||
PrepareApply(
|
PrepareApply(
|
||||||
flags helm.Options, stateFile *state.State, serviceAccURI string, masterSecret uri.MasterSecret, openStackCfg *config.OpenStackConfig,
|
flags helm.Options, stateFile *state.State, serviceAccURI string, masterSecret uri.MasterSecret,
|
||||||
) (
|
) (
|
||||||
helm.Applier, bool, error)
|
helm.Applier, bool, error)
|
||||||
}
|
}
|
||||||
|
@ -467,7 +467,6 @@ go_library(
|
|||||||
"//internal/cloud/gcpshared",
|
"//internal/cloud/gcpshared",
|
||||||
"//internal/cloud/openstack",
|
"//internal/cloud/openstack",
|
||||||
"//internal/compatibility",
|
"//internal/compatibility",
|
||||||
"//internal/config",
|
|
||||||
"//internal/constants",
|
"//internal/constants",
|
||||||
"//internal/constellation/helm/imageversion",
|
"//internal/constellation/helm/imageversion",
|
||||||
"//internal/constellation/state",
|
"//internal/constellation/state",
|
||||||
|
@ -35,7 +35,6 @@ import (
|
|||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constellation/state"
|
"github.com/edgelesssys/constellation/v2/internal/constellation/state"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
@ -91,13 +90,14 @@ type Options struct {
|
|||||||
MicroserviceVersion semver.Semver
|
MicroserviceVersion semver.Semver
|
||||||
HelmWaitMode WaitMode
|
HelmWaitMode WaitMode
|
||||||
ApplyTimeout time.Duration
|
ApplyTimeout time.Duration
|
||||||
|
OpenStackValues *OpenStackValues
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareApply loads the charts and returns the executor to apply them.
|
// PrepareApply loads the charts and returns the executor to apply them.
|
||||||
func (h Client) PrepareApply(
|
func (h Client) PrepareApply(
|
||||||
flags Options, stateFile *state.State, serviceAccURI string, masterSecret uri.MasterSecret, openStackCfg *config.OpenStackConfig,
|
flags Options, stateFile *state.State, serviceAccURI string, masterSecret uri.MasterSecret,
|
||||||
) (Applier, bool, error) {
|
) (Applier, bool, error) {
|
||||||
releases, err := h.loadReleases(flags.CSP, flags.AttestationVariant, flags.K8sVersion, masterSecret, stateFile, flags, serviceAccURI, openStackCfg)
|
releases, err := h.loadReleases(flags.CSP, flags.AttestationVariant, flags.K8sVersion, masterSecret, stateFile, flags, serviceAccURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, fmt.Errorf("loading Helm releases: %w", err)
|
return nil, false, fmt.Errorf("loading Helm releases: %w", err)
|
||||||
}
|
}
|
||||||
@ -111,11 +111,11 @@ func (h Client) PrepareApply(
|
|||||||
|
|
||||||
func (h Client) loadReleases(
|
func (h Client) loadReleases(
|
||||||
csp cloudprovider.Provider, attestationVariant variant.Variant, k8sVersion versions.ValidK8sVersion, secret uri.MasterSecret,
|
csp cloudprovider.Provider, attestationVariant variant.Variant, k8sVersion versions.ValidK8sVersion, secret uri.MasterSecret,
|
||||||
stateFile *state.State, flags Options, serviceAccURI string, openStackCfg *config.OpenStackConfig,
|
stateFile *state.State, flags Options, serviceAccURI string,
|
||||||
) ([]release, error) {
|
) ([]release, error) {
|
||||||
helmLoader := newLoader(csp, attestationVariant, k8sVersion, stateFile, h.cliVersion)
|
helmLoader := newLoader(csp, attestationVariant, k8sVersion, stateFile, h.cliVersion)
|
||||||
h.log.Debug("Created new Helm loader")
|
h.log.Debug("Created new Helm loader")
|
||||||
return helmLoader.loadReleases(flags.Conformance, flags.DeployCSIDriver, flags.HelmWaitMode, secret, serviceAccURI, openStackCfg)
|
return helmLoader.loadReleases(flags.Conformance, flags.DeployCSIDriver, flags.HelmWaitMode, secret, serviceAccURI, flags.OpenStackValues)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Applier runs the Helm actions.
|
// Applier runs the Helm actions.
|
||||||
|
@ -217,7 +217,7 @@ func TestHelmApply(t *testing.T) {
|
|||||||
SetInfrastructure(state.Infrastructure{UID: "testuid"}).
|
SetInfrastructure(state.Infrastructure{UID: "testuid"}).
|
||||||
SetClusterValues(state.ClusterValues{MeasurementSalt: []byte{0x41}}),
|
SetClusterValues(state.ClusterValues{MeasurementSalt: []byte{0x41}}),
|
||||||
fakeServiceAccURI(csp),
|
fakeServiceAccURI(csp),
|
||||||
uri.MasterSecret{Key: []byte("secret"), Salt: []byte("masterSalt")}, nil)
|
uri.MasterSecret{Key: []byte("secret"), Salt: []byte("masterSalt")})
|
||||||
var upgradeErr *compatibility.InvalidUpgradeError
|
var upgradeErr *compatibility.InvalidUpgradeError
|
||||||
if tc.expectError {
|
if tc.expectError {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constellation/helm/imageversion"
|
"github.com/edgelesssys/constellation/v2/internal/constellation/helm/imageversion"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constellation/state"
|
"github.com/edgelesssys/constellation/v2/internal/constellation/state"
|
||||||
@ -115,9 +114,17 @@ func newLoader(csp cloudprovider.Provider, attestationVariant variant.Variant, k
|
|||||||
// that the new release is installed after the existing one to avoid name conflicts.
|
// that the new release is installed after the existing one to avoid name conflicts.
|
||||||
type releaseApplyOrder []release
|
type releaseApplyOrder []release
|
||||||
|
|
||||||
|
// OpenStackValues are helm values for OpenStack.
|
||||||
|
type OpenStackValues struct {
|
||||||
|
DeployYawolLoadBalancer bool
|
||||||
|
FloatingIPPoolID string
|
||||||
|
YawolFlavorID string
|
||||||
|
YawolImageID string
|
||||||
|
}
|
||||||
|
|
||||||
// loadReleases loads the embedded helm charts and returns them as a HelmReleases object.
|
// loadReleases loads the embedded helm charts and returns them as a HelmReleases object.
|
||||||
func (i *chartLoader) loadReleases(conformanceMode, deployCSIDriver bool, helmWaitMode WaitMode, masterSecret uri.MasterSecret,
|
func (i *chartLoader) loadReleases(conformanceMode, deployCSIDriver bool, helmWaitMode WaitMode, masterSecret uri.MasterSecret,
|
||||||
serviceAccURI string, openStackCfg *config.OpenStackConfig,
|
serviceAccURI string, openStackValues *OpenStackValues,
|
||||||
) (releaseApplyOrder, error) {
|
) (releaseApplyOrder, error) {
|
||||||
ciliumRelease, err := i.loadRelease(ciliumInfo, helmWaitMode)
|
ciliumRelease, err := i.loadRelease(ciliumInfo, helmWaitMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -143,7 +150,7 @@ func (i *chartLoader) loadReleases(conformanceMode, deployCSIDriver bool, helmWa
|
|||||||
}
|
}
|
||||||
|
|
||||||
svcVals, err := extraConstellationServicesValues(i.csp, i.attestationVariant, masterSecret,
|
svcVals, err := extraConstellationServicesValues(i.csp, i.attestationVariant, masterSecret,
|
||||||
serviceAccURI, i.stateFile.Infrastructure, openStackCfg)
|
serviceAccURI, i.stateFile.Infrastructure, openStackValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("extending constellation-services values: %w", err)
|
return nil, fmt.Errorf("extending constellation-services values: %w", err)
|
||||||
}
|
}
|
||||||
@ -169,13 +176,13 @@ func (i *chartLoader) loadReleases(conformanceMode, deployCSIDriver bool, helmWa
|
|||||||
}
|
}
|
||||||
releases = append(releases, awsRelease)
|
releases = append(releases, awsRelease)
|
||||||
}
|
}
|
||||||
if i.csp == cloudprovider.OpenStack && openStackCfg.DeployYawolLoadBalancer != nil && *openStackCfg.DeployYawolLoadBalancer {
|
if i.csp == cloudprovider.OpenStack && openStackValues != nil && openStackValues.DeployYawolLoadBalancer {
|
||||||
yawolRelease, err := i.loadRelease(yawolLBControllerInfo, WaitModeNone)
|
yawolRelease, err := i.loadRelease(yawolLBControllerInfo, WaitModeNone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("loading yawol chart: %w", err)
|
return nil, fmt.Errorf("loading yawol chart: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
yawolVals, err := extraYawolValues(serviceAccURI, i.stateFile.Infrastructure, openStackCfg)
|
yawolVals, err := extraYawolValues(serviceAccURI, i.stateFile.Infrastructure, openStackValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("extending yawol chart values: %w", err)
|
return nil, fmt.Errorf("extending yawol chart values: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -175,6 +175,19 @@ func TestConstellationServices(t *testing.T) {
|
|||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
var openstackValues *OpenStackValues
|
||||||
|
if tc.config.Provider.OpenStack != nil {
|
||||||
|
var deploy bool
|
||||||
|
if tc.config.Provider.OpenStack.DeployYawolLoadBalancer != nil {
|
||||||
|
deploy = *tc.config.Provider.OpenStack.DeployYawolLoadBalancer
|
||||||
|
}
|
||||||
|
openstackValues = &OpenStackValues{
|
||||||
|
DeployYawolLoadBalancer: deploy,
|
||||||
|
FloatingIPPoolID: tc.config.Provider.OpenStack.FloatingIPPoolID,
|
||||||
|
YawolFlavorID: tc.config.Provider.OpenStack.YawolFlavorID,
|
||||||
|
YawolImageID: tc.config.Provider.OpenStack.YawolImageID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
chartLoader := chartLoader{
|
chartLoader := chartLoader{
|
||||||
csp: tc.config.GetProvider(),
|
csp: tc.config.GetProvider(),
|
||||||
@ -199,7 +212,7 @@ func TestConstellationServices(t *testing.T) {
|
|||||||
UID: "uid",
|
UID: "uid",
|
||||||
Azure: &state.Azure{},
|
Azure: &state.Azure{},
|
||||||
GCP: &state.GCP{},
|
GCP: &state.GCP{},
|
||||||
}, tc.config.Provider.OpenStack)
|
}, openstackValues)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
values = mergeMaps(values, extraVals)
|
values = mergeMaps(values, extraVals)
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/gcpshared"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/gcpshared"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/openstack"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/openstack"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constellation/state"
|
"github.com/edgelesssys/constellation/v2/internal/constellation/state"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
|
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
|
||||||
@ -83,7 +82,7 @@ func extraCiliumValues(provider cloudprovider.Provider, conformanceMode bool, ou
|
|||||||
// Values set inside this function are only applied during init, not during upgrade.
|
// Values set inside this function are only applied during init, not during upgrade.
|
||||||
func extraConstellationServicesValues(
|
func extraConstellationServicesValues(
|
||||||
csp cloudprovider.Provider, attestationVariant variant.Variant, masterSecret uri.MasterSecret, serviceAccURI string,
|
csp cloudprovider.Provider, attestationVariant variant.Variant, masterSecret uri.MasterSecret, serviceAccURI string,
|
||||||
output state.Infrastructure, openStackCfg *config.OpenStackConfig,
|
output state.Infrastructure, openStackCfg *OpenStackValues,
|
||||||
) (map[string]any, error) {
|
) (map[string]any, error) {
|
||||||
extraVals := map[string]any{}
|
extraVals := map[string]any{}
|
||||||
extraVals["join-service"] = map[string]any{
|
extraVals["join-service"] = map[string]any{
|
||||||
@ -152,7 +151,7 @@ func extraConstellationServicesValues(
|
|||||||
|
|
||||||
// extraYawolValues extends the given values map by some values depending on user input.
|
// extraYawolValues extends the given values map by some values depending on user input.
|
||||||
// Values set inside this function are only applied during init, not during upgrade.
|
// Values set inside this function are only applied during init, not during upgrade.
|
||||||
func extraYawolValues(serviceAccURI string, output state.Infrastructure, openStackCfg *config.OpenStackConfig) (map[string]any, error) {
|
func extraYawolValues(serviceAccURI string, output state.Infrastructure, openStackCfg *OpenStackValues) (map[string]any, error) {
|
||||||
extraVals := map[string]any{}
|
extraVals := map[string]any{}
|
||||||
|
|
||||||
creds, err := openstack.AccountKeyFromURI(serviceAccURI)
|
creds, err := openstack.AccountKeyFromURI(serviceAccURI)
|
||||||
@ -163,7 +162,7 @@ func extraYawolValues(serviceAccURI string, output state.Infrastructure, openSta
|
|||||||
extraVals["yawol-config"] = map[string]any{
|
extraVals["yawol-config"] = map[string]any{
|
||||||
"secretData": yawolIni,
|
"secretData": yawolIni,
|
||||||
}
|
}
|
||||||
if openStackCfg.DeployYawolLoadBalancer != nil && *openStackCfg.DeployYawolLoadBalancer {
|
if openStackCfg != nil && openStackCfg.DeployYawolLoadBalancer {
|
||||||
extraVals["yawol-controller"] = map[string]any{
|
extraVals["yawol-controller"] = map[string]any{
|
||||||
"yawolOSSecretName": "yawolkey",
|
"yawolOSSecretName": "yawolkey",
|
||||||
// has to be larger than ~30s to account for slow OpenStack API calls.
|
// has to be larger than ~30s to account for slow OpenStack API calls.
|
||||||
|
@ -33,6 +33,7 @@ data "constellation_attestation" "test" {
|
|||||||
* `azure-sev-snp`
|
* `azure-sev-snp`
|
||||||
* `azure-tdx`
|
* `azure-tdx`
|
||||||
* `gcp-sev-es`
|
* `gcp-sev-es`
|
||||||
|
* `qemu-vtpm`
|
||||||
- `csp` (String) CSP (Cloud Service Provider) to use. (e.g. `azure`)
|
- `csp` (String) CSP (Cloud Service Provider) to use. (e.g. `azure`)
|
||||||
See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview/clouds) that Constellation supports.
|
See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview/clouds) that Constellation supports.
|
||||||
- `image` (Attributes) Constellation OS Image to use on the nodes. (see [below for nested schema](#nestedatt--image))
|
- `image` (Attributes) Constellation OS Image to use on the nodes. (see [below for nested schema](#nestedatt--image))
|
||||||
@ -82,6 +83,7 @@ Read-Only:
|
|||||||
* `azure-sev-snp`
|
* `azure-sev-snp`
|
||||||
* `azure-tdx`
|
* `azure-tdx`
|
||||||
* `gcp-sev-es`
|
* `gcp-sev-es`
|
||||||
|
* `qemu-vtpm`
|
||||||
|
|
||||||
<a id="nestedatt--attestation--azure_firmware_signer_config"></a>
|
<a id="nestedatt--attestation--azure_firmware_signer_config"></a>
|
||||||
### Nested Schema for `attestation.azure_firmware_signer_config`
|
### Nested Schema for `attestation.azure_firmware_signer_config`
|
||||||
|
@ -32,6 +32,7 @@ data "constellation_image" "example" {
|
|||||||
* `azure-sev-snp`
|
* `azure-sev-snp`
|
||||||
* `azure-tdx`
|
* `azure-tdx`
|
||||||
* `gcp-sev-es`
|
* `gcp-sev-es`
|
||||||
|
* `qemu-vtpm`
|
||||||
- `csp` (String) CSP (Cloud Service Provider) to use. (e.g. `azure`)
|
- `csp` (String) CSP (Cloud Service Provider) to use. (e.g. `azure`)
|
||||||
See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview/clouds) that Constellation supports.
|
See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview/clouds) that Constellation supports.
|
||||||
|
|
||||||
|
@ -86,6 +86,7 @@ See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview
|
|||||||
- `gcp` (Attributes) GCP-specific configuration. (see [below for nested schema](#nestedatt--gcp))
|
- `gcp` (Attributes) GCP-specific configuration. (see [below for nested schema](#nestedatt--gcp))
|
||||||
- `in_cluster_endpoint` (String) The endpoint of the cluster. When not set, the out-of-cluster endpoint is used.
|
- `in_cluster_endpoint` (String) The endpoint of the cluster. When not set, the out-of-cluster endpoint is used.
|
||||||
- `license_id` (String) Constellation license ID. When not set, the community license is used.
|
- `license_id` (String) Constellation license ID. When not set, the community license is used.
|
||||||
|
- `openstack` (Attributes) OpenStack-specific configuration. (see [below for nested schema](#nestedatt--openstack))
|
||||||
|
|
||||||
### Read-Only
|
### Read-Only
|
||||||
|
|
||||||
@ -110,6 +111,7 @@ Required:
|
|||||||
* `azure-sev-snp`
|
* `azure-sev-snp`
|
||||||
* `azure-tdx`
|
* `azure-tdx`
|
||||||
* `gcp-sev-es`
|
* `gcp-sev-es`
|
||||||
|
* `qemu-vtpm`
|
||||||
|
|
||||||
Optional:
|
Optional:
|
||||||
|
|
||||||
@ -211,6 +213,24 @@ Required:
|
|||||||
- `project_id` (String) ID of the GCP project the cluster resides in.
|
- `project_id` (String) ID of the GCP project the cluster resides in.
|
||||||
- `service_account_key` (String) Base64-encoded private key JSON object of the service account used within the cluster.
|
- `service_account_key` (String) Base64-encoded private key JSON object of the service account used within the cluster.
|
||||||
|
|
||||||
|
|
||||||
|
<a id="nestedatt--openstack"></a>
|
||||||
|
### Nested Schema for `openstack`
|
||||||
|
|
||||||
|
Required:
|
||||||
|
|
||||||
|
- `cloud` (String) Name of the cloud in the clouds.yaml file.
|
||||||
|
- `floating_ip_pool_id` (String) Floating IP pool to use for the VMs.
|
||||||
|
- `network_id` (String) OpenStack network ID to use for the VMs.
|
||||||
|
- `subnet_id` (String) OpenStack subnet ID to use for the VMs.
|
||||||
|
|
||||||
|
Optional:
|
||||||
|
|
||||||
|
- `clouds_yaml_path` (String) Path to the clouds.yaml file.
|
||||||
|
- `deploy_yawol_load_balancer` (Boolean) Whether to deploy a YAWOL load balancer.
|
||||||
|
- `yawol_flavor_id` (String) OpenStack flavor used by the yawollet.
|
||||||
|
- `yawol_image_id` (String) OpenStack OS image used by the yawollet.
|
||||||
|
|
||||||
## Import
|
## Import
|
||||||
|
|
||||||
Import is supported using the following syntax:
|
Import is supported using the following syntax:
|
||||||
|
128
terraform-provider-constellation/examples/full/stackit/main.tf
Normal file
128
terraform-provider-constellation/examples/full/stackit/main.tf
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
constellation = {
|
||||||
|
source = "edgelesssys/constellation"
|
||||||
|
version = "0.0.0" // replace with the version you want to use
|
||||||
|
}
|
||||||
|
random = {
|
||||||
|
source = "hashicorp/random"
|
||||||
|
version = "3.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
locals {
|
||||||
|
name = "constell"
|
||||||
|
image_version = "vX.Y.Z"
|
||||||
|
kubernetes_version = "vX.Y.Z"
|
||||||
|
microservice_version = "vX.Y.Z"
|
||||||
|
csp = "stackit"
|
||||||
|
attestation_variant = "qemu-vtpm"
|
||||||
|
zone = "eu01-1"
|
||||||
|
cloud = "stackit"
|
||||||
|
clouds_yaml_path = "~/.config/openstack/clouds.yaml"
|
||||||
|
floating_ip_pool_id = "970ace5c-458f-484a-a660-0903bcfd91ad"
|
||||||
|
stackit_project_id = "" // replace with the STACKIT project id
|
||||||
|
control_plane_count = 3
|
||||||
|
worker_count = 2
|
||||||
|
instance_type = "m1a.8cd"
|
||||||
|
deploy_yawol_load_balancer = true
|
||||||
|
yawol_image_id = "bcd6c13e-75d1-4c3f-bf0f-8f83580cc1be"
|
||||||
|
yawol_flavor_id = "3b11b27e-6c73-470d-b595-1d85b95a8cdf"
|
||||||
|
|
||||||
|
master_secret = random_bytes.master_secret.hex
|
||||||
|
master_secret_salt = random_bytes.master_secret_salt.hex
|
||||||
|
measurement_salt = random_bytes.measurement_salt.hex
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_bytes" "master_secret" {
|
||||||
|
length = 32
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_bytes" "master_secret_salt" {
|
||||||
|
length = 32
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_bytes" "measurement_salt" {
|
||||||
|
length = 32
|
||||||
|
}
|
||||||
|
|
||||||
|
module "stackit_infrastructure" {
|
||||||
|
// replace $VERSION with the Constellation version you want to use, e.g., v2.14.0
|
||||||
|
source = "https://github.com/edgelesssys/constellation/releases/download/$VERSION/terraform-module.zip//terraform-module/openstack"
|
||||||
|
name = local.name
|
||||||
|
node_groups = {
|
||||||
|
control_plane_default = {
|
||||||
|
role = "control-plane"
|
||||||
|
flavor_id = local.instance_type
|
||||||
|
state_disk_size = 30
|
||||||
|
state_disk_type = "storage_premium_perf6"
|
||||||
|
initial_count = local.control_plane_count
|
||||||
|
zone = local.zone
|
||||||
|
},
|
||||||
|
worker_default = {
|
||||||
|
role = "worker"
|
||||||
|
flavor_id = local.instance_type
|
||||||
|
state_disk_size = 30
|
||||||
|
state_disk_type = "storage_premium_perf6"
|
||||||
|
initial_count = local.worker_count
|
||||||
|
zone = local.zone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
image_id = data.constellation_image.bar.image.reference
|
||||||
|
debug = false
|
||||||
|
cloud = local.cloud
|
||||||
|
openstack_clouds_yaml_path = local.clouds_yaml_path
|
||||||
|
floating_ip_pool_id = local.floating_ip_pool_id
|
||||||
|
stackit_project_id = local.stackit_project_id
|
||||||
|
}
|
||||||
|
|
||||||
|
data "constellation_attestation" "foo" {
|
||||||
|
csp = local.csp
|
||||||
|
attestation_variant = local.attestation_variant
|
||||||
|
image = data.constellation_image.bar.image
|
||||||
|
}
|
||||||
|
|
||||||
|
data "constellation_image" "bar" {
|
||||||
|
csp = local.csp
|
||||||
|
attestation_variant = local.attestation_variant
|
||||||
|
version = local.image_version
|
||||||
|
marketplace_image = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "constellation_cluster" "stackit_example" {
|
||||||
|
csp = local.csp
|
||||||
|
name = module.stackit_infrastructure.name
|
||||||
|
uid = module.stackit_infrastructure.uid
|
||||||
|
image = data.constellation_image.bar.image
|
||||||
|
attestation = data.constellation_attestation.foo.attestation
|
||||||
|
kubernetes_version = local.kubernetes_version
|
||||||
|
constellation_microservice_version = local.microservice_version
|
||||||
|
init_secret = module.stackit_infrastructure.init_secret
|
||||||
|
master_secret = local.master_secret
|
||||||
|
master_secret_salt = local.master_secret_salt
|
||||||
|
measurement_salt = local.measurement_salt
|
||||||
|
out_of_cluster_endpoint = module.stackit_infrastructure.out_of_cluster_endpoint
|
||||||
|
in_cluster_endpoint = module.stackit_infrastructure.in_cluster_endpoint
|
||||||
|
api_server_cert_sans = module.stackit_infrastructure.api_server_cert_sans
|
||||||
|
openstack = {
|
||||||
|
cloud = local.cloud
|
||||||
|
clouds_yaml_path = local.clouds_yaml_path
|
||||||
|
floating_ip_pool_id = local.floating_ip_pool_id
|
||||||
|
deploy_yawol_load_balancer = local.deploy_yawol_load_balancer
|
||||||
|
yawol_image_id = local.yawol_image_id
|
||||||
|
yawol_flavor_id = local.yawol_flavor_id
|
||||||
|
network_id = module.stackit_infrastructure.network_id
|
||||||
|
subnet_id = module.stackit_infrastructure.lb_subnetwork_id
|
||||||
|
}
|
||||||
|
network_config = {
|
||||||
|
ip_cidr_node = module.stackit_infrastructure.ip_cidr_node
|
||||||
|
ip_cidr_service = "10.96.0.0/12"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output "kubeconfig" {
|
||||||
|
value = constellation_cluster.stackit_example.kubeconfig
|
||||||
|
sensitive = true
|
||||||
|
description = "KubeConfig for the Constellation cluster."
|
||||||
|
}
|
@ -23,6 +23,8 @@ go_library(
|
|||||||
"//internal/attestation/variant",
|
"//internal/attestation/variant",
|
||||||
"//internal/cloud/azureshared",
|
"//internal/cloud/azureshared",
|
||||||
"//internal/cloud/cloudprovider",
|
"//internal/cloud/cloudprovider",
|
||||||
|
"//internal/cloud/openstack",
|
||||||
|
"//internal/cloud/openstack/clouds",
|
||||||
"//internal/compatibility",
|
"//internal/compatibility",
|
||||||
"//internal/config",
|
"//internal/config",
|
||||||
"//internal/constants",
|
"//internal/constants",
|
||||||
@ -30,6 +32,7 @@ go_library(
|
|||||||
"//internal/constellation/helm",
|
"//internal/constellation/helm",
|
||||||
"//internal/constellation/kubecmd",
|
"//internal/constellation/kubecmd",
|
||||||
"//internal/constellation/state",
|
"//internal/constellation/state",
|
||||||
|
"//internal/file",
|
||||||
"//internal/grpc/dialer",
|
"//internal/grpc/dialer",
|
||||||
"//internal/imagefetcher",
|
"//internal/imagefetcher",
|
||||||
"//internal/kms/uri",
|
"//internal/kms/uri",
|
||||||
@ -53,6 +56,7 @@ go_library(
|
|||||||
"@com_github_hashicorp_terraform_plugin_framework//types/basetypes",
|
"@com_github_hashicorp_terraform_plugin_framework//types/basetypes",
|
||||||
"@com_github_hashicorp_terraform_plugin_framework_validators//stringvalidator",
|
"@com_github_hashicorp_terraform_plugin_framework_validators//stringvalidator",
|
||||||
"@com_github_hashicorp_terraform_plugin_log//tflog",
|
"@com_github_hashicorp_terraform_plugin_log//tflog",
|
||||||
|
"@com_github_spf13_afero//:afero",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -110,6 +110,58 @@ func TestAccAttestationSource(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"STACKIT qemu-vtpm success": {
|
||||||
|
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||||
|
PreCheck: bazelPreCheck,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testingConfig + `
|
||||||
|
data "constellation_attestation" "test" {
|
||||||
|
csp = "stackit"
|
||||||
|
attestation_variant = "qemu-vtpm"
|
||||||
|
image = {
|
||||||
|
version = "v2.13.0"
|
||||||
|
reference = "v2.13.0"
|
||||||
|
short_path = "v2.13.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.variant", "qemu-vtpm"),
|
||||||
|
resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.bootloader_version", "0"), // since this is not supported on STACKIT, we expect 0
|
||||||
|
|
||||||
|
resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.measurements.15.expected", "0000000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.measurements.15.warn_only", "false"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"openstack qemu-vtpm success": {
|
||||||
|
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||||
|
PreCheck: bazelPreCheck,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testingConfig + `
|
||||||
|
data "constellation_attestation" "test" {
|
||||||
|
csp = "openstack"
|
||||||
|
attestation_variant = "qemu-vtpm"
|
||||||
|
image = {
|
||||||
|
version = "v2.13.0"
|
||||||
|
reference = "v2.13.0"
|
||||||
|
short_path = "v2.13.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.variant", "qemu-vtpm"),
|
||||||
|
resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.bootloader_version", "0"), // since this is not supported on OpenStack, we expect 0
|
||||||
|
|
||||||
|
resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.measurements.15.expected", "0000000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.measurements.15.warn_only", "false"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range testCases {
|
for name, tc := range testCases {
|
||||||
|
@ -26,6 +26,8 @@ import (
|
|||||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/azureshared"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/azureshared"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
|
openstackshared "github.com/edgelesssys/constellation/v2/internal/cloud/openstack"
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/cloud/openstack/clouds"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/compatibility"
|
"github.com/edgelesssys/constellation/v2/internal/compatibility"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
@ -33,6 +35,7 @@ import (
|
|||||||
"github.com/edgelesssys/constellation/v2/internal/constellation/helm"
|
"github.com/edgelesssys/constellation/v2/internal/constellation/helm"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constellation/kubecmd"
|
"github.com/edgelesssys/constellation/v2/internal/constellation/kubecmd"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constellation/state"
|
"github.com/edgelesssys/constellation/v2/internal/constellation/state"
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/grpc/dialer"
|
"github.com/edgelesssys/constellation/v2/internal/grpc/dialer"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
|
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/license"
|
"github.com/edgelesssys/constellation/v2/internal/license"
|
||||||
@ -50,6 +53,7 @@ import (
|
|||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
|
"github.com/spf13/afero"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -96,6 +100,7 @@ type ClusterResourceModel struct {
|
|||||||
Attestation types.Object `tfsdk:"attestation"`
|
Attestation types.Object `tfsdk:"attestation"`
|
||||||
GCP types.Object `tfsdk:"gcp"`
|
GCP types.Object `tfsdk:"gcp"`
|
||||||
Azure types.Object `tfsdk:"azure"`
|
Azure types.Object `tfsdk:"azure"`
|
||||||
|
OpenStack types.Object `tfsdk:"openstack"`
|
||||||
|
|
||||||
OwnerID types.String `tfsdk:"owner_id"`
|
OwnerID types.String `tfsdk:"owner_id"`
|
||||||
ClusterID types.String `tfsdk:"cluster_id"`
|
ClusterID types.String `tfsdk:"cluster_id"`
|
||||||
@ -129,6 +134,17 @@ type azureAttribute struct {
|
|||||||
LoadBalancerName string `tfsdk:"load_balancer_name"`
|
LoadBalancerName string `tfsdk:"load_balancer_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type openStackAttribute struct {
|
||||||
|
Cloud string `tfsdk:"cloud"`
|
||||||
|
CloudsYAMLPath string `tfsdk:"clouds_yaml_path"`
|
||||||
|
FloatingIPPoolID string `tfsdk:"floating_ip_pool_id"`
|
||||||
|
DeployYawolLoadBalancer bool `tfsdk:"deploy_yawol_load_balancer"`
|
||||||
|
YawolImageID string `tfsdk:"yawol_image_id"`
|
||||||
|
YawolFlavorID string `tfsdk:"yawol_flavor_id"`
|
||||||
|
NetworkID string `tfsdk:"network_id"`
|
||||||
|
SubnetID string `tfsdk:"subnet_id"`
|
||||||
|
}
|
||||||
|
|
||||||
// extraMicroservicesAttribute is the extra microservices attribute's data model.
|
// extraMicroservicesAttribute is the extra microservices attribute's data model.
|
||||||
type extraMicroservicesAttribute struct {
|
type extraMicroservicesAttribute struct {
|
||||||
CSIDriver bool `tfsdk:"csi_driver"`
|
CSIDriver bool `tfsdk:"csi_driver"`
|
||||||
@ -333,6 +349,53 @@ func (r *ClusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"openstack": schema.SingleNestedAttribute{
|
||||||
|
MarkdownDescription: "OpenStack-specific configuration.",
|
||||||
|
Description: "OpenStack-specific configuration.",
|
||||||
|
Optional: true,
|
||||||
|
Attributes: map[string]schema.Attribute{
|
||||||
|
"cloud": schema.StringAttribute{
|
||||||
|
MarkdownDescription: "Name of the cloud in the clouds.yaml file.",
|
||||||
|
Description: "Name of the cloud in the clouds.yaml file.",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"clouds_yaml_path": schema.StringAttribute{
|
||||||
|
MarkdownDescription: "Path to the clouds.yaml file.",
|
||||||
|
Description: "Path to the clouds.yaml file.",
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"floating_ip_pool_id": schema.StringAttribute{
|
||||||
|
MarkdownDescription: "Floating IP pool to use for the VMs.",
|
||||||
|
Description: "Floating IP pool to use for the VMs.",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"deploy_yawol_load_balancer": schema.BoolAttribute{
|
||||||
|
MarkdownDescription: "Whether to deploy a YAWOL load balancer.",
|
||||||
|
Description: "Whether to deploy a YAWOL load balancer.",
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"yawol_image_id": schema.StringAttribute{
|
||||||
|
MarkdownDescription: "OpenStack OS image used by the yawollet.",
|
||||||
|
Description: "OpenStack OS image used by the yawollet.",
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"yawol_flavor_id": schema.StringAttribute{
|
||||||
|
MarkdownDescription: "OpenStack flavor used by the yawollet.",
|
||||||
|
Description: "OpenStack flavor used by the yawollet.",
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"network_id": schema.StringAttribute{
|
||||||
|
MarkdownDescription: "OpenStack network ID to use for the VMs.",
|
||||||
|
Description: "OpenStack network ID to use for the VMs.",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"subnet_id": schema.StringAttribute{
|
||||||
|
MarkdownDescription: "OpenStack subnet ID to use for the VMs.",
|
||||||
|
Description: "OpenStack subnet ID to use for the VMs.",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// Computed (output) attributes
|
// Computed (output) attributes
|
||||||
"owner_id": schema.StringAttribute{
|
"owner_id": schema.StringAttribute{
|
||||||
@ -406,6 +469,26 @@ func (r *ClusterResource) ValidateConfig(ctx context.Context, req resource.Valid
|
|||||||
"GCP configuration not allowed", "When csp is not set to 'gcp', setting the 'gcp' configuration has no effect.",
|
"GCP configuration not allowed", "When csp is not set to 'gcp', setting the 'gcp' configuration has no effect.",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenStack Config is required for OpenStack
|
||||||
|
if (strings.EqualFold(data.CSP.ValueString(), cloudprovider.OpenStack.String()) ||
|
||||||
|
strings.EqualFold(data.CSP.ValueString(), "stackit")) &&
|
||||||
|
data.OpenStack.IsNull() {
|
||||||
|
resp.Diagnostics.AddAttributeError(
|
||||||
|
path.Root("openstack"),
|
||||||
|
"OpenStack configuration missing", "When csp is set to 'openstack' or 'stackit', the 'openstack' configuration must be set.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenStack Config should not be set for other CSPs
|
||||||
|
if !strings.EqualFold(data.CSP.ValueString(), cloudprovider.OpenStack.String()) &&
|
||||||
|
!strings.EqualFold(data.CSP.ValueString(), "stackit") &&
|
||||||
|
!data.OpenStack.IsNull() {
|
||||||
|
resp.Diagnostics.AddAttributeWarning(
|
||||||
|
path.Root("openstack"),
|
||||||
|
"OpenStack configuration not allowed", "When csp is not set to 'openstack' or 'stackit', setting the 'openstack' configuration has no effect.",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure configures the resource.
|
// Configure configures the resource.
|
||||||
@ -779,6 +862,7 @@ func (r *ClusterResource) apply(ctx context.Context, data *ClusterResourceModel,
|
|||||||
serviceAccPayload := constellation.ServiceAccountPayload{}
|
serviceAccPayload := constellation.ServiceAccountPayload{}
|
||||||
var gcpConfig gcpAttribute
|
var gcpConfig gcpAttribute
|
||||||
var azureConfig azureAttribute
|
var azureConfig azureAttribute
|
||||||
|
var openStackConfig openStackAttribute
|
||||||
switch csp {
|
switch csp {
|
||||||
case cloudprovider.GCP:
|
case cloudprovider.GCP:
|
||||||
convertDiags = data.GCP.As(ctx, &gcpConfig, basetypes.ObjectAsOptions{})
|
convertDiags = data.GCP.As(ctx, &gcpConfig, basetypes.ObjectAsOptions{})
|
||||||
@ -815,6 +899,33 @@ func (r *ClusterResource) apply(ctx context.Context, data *ClusterResourceModel,
|
|||||||
PreferredAuthMethod: azureshared.AuthMethodUserAssignedIdentity,
|
PreferredAuthMethod: azureshared.AuthMethodUserAssignedIdentity,
|
||||||
UamiResourceID: azureConfig.UamiResourceID,
|
UamiResourceID: azureConfig.UamiResourceID,
|
||||||
}
|
}
|
||||||
|
case cloudprovider.OpenStack:
|
||||||
|
convertDiags = data.OpenStack.As(ctx, &openStackConfig, basetypes.ObjectAsOptions{})
|
||||||
|
diags.Append(convertDiags...)
|
||||||
|
if diags.HasError() {
|
||||||
|
return diags
|
||||||
|
}
|
||||||
|
cloudsYAML, err := clouds.ReadCloudsYAML(file.NewHandler(afero.NewOsFs()), openStackConfig.CloudsYAMLPath)
|
||||||
|
if err != nil {
|
||||||
|
diags.AddError("Reading clouds.yaml", err.Error())
|
||||||
|
return diags
|
||||||
|
}
|
||||||
|
cloud, ok := cloudsYAML.Clouds[openStackConfig.Cloud]
|
||||||
|
if !ok {
|
||||||
|
diags.AddError("Reading clouds.yaml", fmt.Sprintf("Cloud %s not found in clouds.yaml", openStackConfig.Cloud))
|
||||||
|
return diags
|
||||||
|
}
|
||||||
|
serviceAccPayload.OpenStack = openstackshared.AccountKey{
|
||||||
|
AuthURL: cloud.AuthInfo.AuthURL,
|
||||||
|
Username: cloud.AuthInfo.Username,
|
||||||
|
Password: cloud.AuthInfo.Password,
|
||||||
|
ProjectID: cloud.AuthInfo.ProjectID,
|
||||||
|
ProjectName: cloud.AuthInfo.ProjectName,
|
||||||
|
UserDomainName: cloud.AuthInfo.UserDomainName,
|
||||||
|
ProjectDomainName: cloud.AuthInfo.ProjectDomainName,
|
||||||
|
RegionName: cloud.RegionName,
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
serviceAccURI, err := constellation.MarshalServiceAccountURI(csp, serviceAccPayload)
|
serviceAccURI, err := constellation.MarshalServiceAccountURI(csp, serviceAccPayload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -861,6 +972,11 @@ func (r *ClusterResource) apply(ctx context.Context, data *ClusterResourceModel,
|
|||||||
ProjectID: gcpConfig.ProjectID,
|
ProjectID: gcpConfig.ProjectID,
|
||||||
IPCidrPod: networkCfg.IPCidrPod.ValueString(),
|
IPCidrPod: networkCfg.IPCidrPod.ValueString(),
|
||||||
}
|
}
|
||||||
|
case cloudprovider.OpenStack:
|
||||||
|
stateFile.Infrastructure.OpenStack = &state.OpenStack{
|
||||||
|
NetworkID: openStackConfig.NetworkID,
|
||||||
|
SubnetID: openStackConfig.SubnetID,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check license
|
// Check license
|
||||||
@ -937,6 +1053,14 @@ func (r *ClusterResource) apply(ctx context.Context, data *ClusterResourceModel,
|
|||||||
masterSecret: secrets.masterSecret,
|
masterSecret: secrets.masterSecret,
|
||||||
serviceAccURI: serviceAccURI,
|
serviceAccURI: serviceAccURI,
|
||||||
}
|
}
|
||||||
|
if csp == cloudprovider.OpenStack {
|
||||||
|
payload.openStackHelmValues = &helm.OpenStackValues{
|
||||||
|
DeployYawolLoadBalancer: openStackConfig.DeployYawolLoadBalancer,
|
||||||
|
FloatingIPPoolID: openStackConfig.FloatingIPPoolID,
|
||||||
|
YawolImageID: openStackConfig.YawolImageID,
|
||||||
|
YawolFlavorID: openStackConfig.YawolFlavorID,
|
||||||
|
}
|
||||||
|
}
|
||||||
helmDiags := r.applyHelmCharts(ctx, applier, payload, stateFile)
|
helmDiags := r.applyHelmCharts(ctx, applier, payload, stateFile)
|
||||||
diags.Append(helmDiags...)
|
diags.Append(helmDiags...)
|
||||||
if diags.HasError() {
|
if diags.HasError() {
|
||||||
@ -1063,6 +1187,7 @@ type applyHelmChartsPayload struct {
|
|||||||
DeployCSIDriver bool // Whether to deploy the CSI driver.
|
DeployCSIDriver bool // Whether to deploy the CSI driver.
|
||||||
masterSecret uri.MasterSecret // master secret of the cluster.
|
masterSecret uri.MasterSecret // master secret of the cluster.
|
||||||
serviceAccURI string // URI of the service account used within the cluster.
|
serviceAccURI string // URI of the service account used within the cluster.
|
||||||
|
openStackHelmValues *helm.OpenStackValues // OpenStack-specific Helm values.
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyHelmCharts applies the Helm charts to the cluster.
|
// applyHelmCharts applies the Helm charts to the cluster.
|
||||||
@ -1083,10 +1208,11 @@ func (r *ClusterResource) applyHelmCharts(ctx context.Context, applier *constell
|
|||||||
// Allow destructive changes to the cluster.
|
// Allow destructive changes to the cluster.
|
||||||
// The user has previously been warned about this when planning a microservice version change.
|
// The user has previously been warned about this when planning a microservice version change.
|
||||||
AllowDestructive: helm.AllowDestructive,
|
AllowDestructive: helm.AllowDestructive,
|
||||||
|
OpenStackValues: payload.openStackHelmValues,
|
||||||
}
|
}
|
||||||
|
|
||||||
executor, _, err := applier.PrepareHelmCharts(options, state,
|
executor, _, err := applier.PrepareHelmCharts(options, state,
|
||||||
payload.serviceAccURI, payload.masterSecret, nil)
|
payload.serviceAccURI, payload.masterSecret)
|
||||||
var upgradeErr *compatibility.InvalidUpgradeError
|
var upgradeErr *compatibility.InvalidUpgradeError
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.As(err, &upgradeErr) {
|
if !errors.As(err, &upgradeErr) {
|
||||||
|
@ -489,6 +489,68 @@ func TestAccClusterResource(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"stackit config missing": {
|
||||||
|
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithVersion(providerVersion),
|
||||||
|
PreCheck: bazelPreCheck,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: fullClusterTestingConfig(t, "openstack") + fmt.Sprintf(`
|
||||||
|
resource "constellation_cluster" "test" {
|
||||||
|
csp = "stackit"
|
||||||
|
name = "constell"
|
||||||
|
uid = "test"
|
||||||
|
image = data.constellation_image.bar.image
|
||||||
|
attestation = data.constellation_attestation.foo.attestation
|
||||||
|
init_secret = "deadbeef"
|
||||||
|
master_secret = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
|
||||||
|
master_secret_salt = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
|
||||||
|
measurement_salt = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
|
||||||
|
out_of_cluster_endpoint = "192.0.2.1"
|
||||||
|
in_cluster_endpoint = "192.0.2.1"
|
||||||
|
network_config = {
|
||||||
|
ip_cidr_node = "0.0.0.0/24"
|
||||||
|
ip_cidr_service = "0.0.0.0/24"
|
||||||
|
ip_cidr_pod = "0.0.0.0/24"
|
||||||
|
}
|
||||||
|
kubernetes_version = "%s"
|
||||||
|
constellation_microservice_version = "%s"
|
||||||
|
}
|
||||||
|
`, versions.Default, providerVersion),
|
||||||
|
ExpectError: regexp.MustCompile(".*When csp is set to 'openstack' or 'stackit', the 'openstack' configuration\nmust be set.*"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"openstack config missing": {
|
||||||
|
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithVersion(providerVersion),
|
||||||
|
PreCheck: bazelPreCheck,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: fullClusterTestingConfig(t, "openstack") + fmt.Sprintf(`
|
||||||
|
resource "constellation_cluster" "test" {
|
||||||
|
csp = "openstack"
|
||||||
|
name = "constell"
|
||||||
|
uid = "test"
|
||||||
|
image = data.constellation_image.bar.image
|
||||||
|
attestation = data.constellation_attestation.foo.attestation
|
||||||
|
init_secret = "deadbeef"
|
||||||
|
master_secret = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
|
||||||
|
master_secret_salt = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
|
||||||
|
measurement_salt = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
|
||||||
|
out_of_cluster_endpoint = "192.0.2.1"
|
||||||
|
in_cluster_endpoint = "192.0.2.1"
|
||||||
|
network_config = {
|
||||||
|
ip_cidr_node = "0.0.0.0/24"
|
||||||
|
ip_cidr_service = "0.0.0.0/24"
|
||||||
|
ip_cidr_pod = "0.0.0.0/24"
|
||||||
|
}
|
||||||
|
kubernetes_version = "%s"
|
||||||
|
constellation_microservice_version = "%s"
|
||||||
|
}
|
||||||
|
`, versions.Default, providerVersion),
|
||||||
|
ExpectError: regexp.MustCompile(".*When csp is set to 'openstack' or 'stackit', the 'openstack' configuration\nmust be set.*"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range testCases {
|
for name, tc := range testCases {
|
||||||
@ -547,6 +609,19 @@ func fullClusterTestingConfig(t *testing.T, csp string) string {
|
|||||||
attestation_variant = "gcp-sev-es"
|
attestation_variant = "gcp-sev-es"
|
||||||
image = data.constellation_image.bar.image
|
image = data.constellation_image.bar.image
|
||||||
}`, image)
|
}`, image)
|
||||||
|
case "openstack":
|
||||||
|
return providerConfig + fmt.Sprintf(`
|
||||||
|
data "constellation_image" "bar" {
|
||||||
|
version = "%s"
|
||||||
|
attestation_variant = "qemu-vtpm"
|
||||||
|
csp = "openstack"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "constellation_attestation" "foo" {
|
||||||
|
csp = "openstack"
|
||||||
|
attestation_variant = "qemu-vtpm"
|
||||||
|
image = data.constellation_image.bar.image
|
||||||
|
}`, image)
|
||||||
default:
|
default:
|
||||||
t.Fatal("unknown csp")
|
t.Fatal("unknown csp")
|
||||||
return ""
|
return ""
|
||||||
|
@ -122,6 +122,10 @@ func convertFromTfAttestationCfg(tfAttestation attestationAttribute, attestation
|
|||||||
attestationConfig = &config.GCPSEVES{
|
attestationConfig = &config.GCPSEVES{
|
||||||
Measurements: c11nMeasurements,
|
Measurements: c11nMeasurements,
|
||||||
}
|
}
|
||||||
|
case variant.QEMUVTPM{}:
|
||||||
|
attestationConfig = &config.QEMUVTPM{
|
||||||
|
Measurements: c11nMeasurements,
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown attestation variant: %s", attestationVariant)
|
return nil, fmt.Errorf("unknown attestation variant: %s", attestationVariant)
|
||||||
}
|
}
|
||||||
@ -177,7 +181,7 @@ func convertToTfAttestation(attVar variant.Variant, snpVersions attestationconfi
|
|||||||
XFAM: hex.EncodeToString(tdxCfg.XFAM),
|
XFAM: hex.EncodeToString(tdxCfg.XFAM),
|
||||||
}
|
}
|
||||||
tfAttestation.TDX = tfTdxCfg
|
tfAttestation.TDX = tfTdxCfg
|
||||||
case variant.GCPSEVES{}:
|
case variant.GCPSEVES{}, variant.QEMUVTPM{}:
|
||||||
// no additional fields
|
// no additional fields
|
||||||
default:
|
default:
|
||||||
return tfAttestation, fmt.Errorf("unknown attestation variant: %s", attVar)
|
return tfAttestation, fmt.Errorf("unknown attestation variant: %s", attVar)
|
||||||
|
@ -252,9 +252,10 @@ func (d *ImageDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
|||||||
|
|
||||||
// Save data into Terraform state
|
// Save data into Terraform state
|
||||||
diags := resp.State.SetAttribute(ctx, path.Root("image"), imageAttribute{
|
diags := resp.State.SetAttribute(ctx, path.Root("image"), imageAttribute{
|
||||||
Reference: imageRef,
|
Reference: imageRef,
|
||||||
Version: imageSemver,
|
Version: imageSemver,
|
||||||
ShortPath: apiCompatibleVer.ShortPath(),
|
ShortPath: apiCompatibleVer.ShortPath(),
|
||||||
|
MarketplaceImage: data.MarketplaceImage.ValueBoolPointer(),
|
||||||
})
|
})
|
||||||
resp.Diagnostics.Append(diags...)
|
resp.Diagnostics.Append(diags...)
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
|
@ -141,6 +141,38 @@ func TestAccImageDataSource(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"stackit success": {
|
||||||
|
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||||
|
PreCheck: bazelPreCheck,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testingConfig + `
|
||||||
|
data "constellation_image" "test" {
|
||||||
|
version = "v2.16.0"
|
||||||
|
attestation_variant = "qemu-vtpm"
|
||||||
|
csp = "stackit"
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
Check: resource.TestCheckResourceAttr("data.constellation_image.test", "image.reference", "8ffc1740-1e41-4281-b872-f8088ffd7692"), // should be immutable,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"openstack success": {
|
||||||
|
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||||
|
PreCheck: bazelPreCheck,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testingConfig + `
|
||||||
|
data "constellation_image" "test" {
|
||||||
|
version = "v2.16.0"
|
||||||
|
attestation_variant = "qemu-vtpm"
|
||||||
|
csp = "openstack"
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
Check: resource.TestCheckResourceAttr("data.constellation_image.test", "image.reference", "8ffc1740-1e41-4281-b872-f8088ffd7692"), // should be immutable,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"unknown attestation variant": {
|
"unknown attestation variant": {
|
||||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||||
PreCheck: bazelPreCheck,
|
PreCheck: bazelPreCheck,
|
||||||
|
@ -31,11 +31,12 @@ func newAttestationVariantAttributeSchema(t attributeType) schema.Attribute {
|
|||||||
" * `aws-nitro-tpm`\n" +
|
" * `aws-nitro-tpm`\n" +
|
||||||
" * `azure-sev-snp`\n" +
|
" * `azure-sev-snp`\n" +
|
||||||
" * `azure-tdx`\n" +
|
" * `azure-tdx`\n" +
|
||||||
" * `gcp-sev-es`\n",
|
" * `gcp-sev-es`\n" +
|
||||||
|
" * `qemu-vtpm`\n",
|
||||||
Required: isInput,
|
Required: isInput,
|
||||||
Computed: !isInput,
|
Computed: !isInput,
|
||||||
Validators: []validator.String{
|
Validators: []validator.String{
|
||||||
stringvalidator.OneOf("aws-sev-snp", "aws-nitro-tpm", "azure-sev-snp", "azure-tdx", "gcp-sev-es"),
|
stringvalidator.OneOf("aws-sev-snp", "aws-nitro-tpm", "azure-sev-snp", "azure-tdx", "gcp-sev-es", "qemu-vtpm"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,7 +48,7 @@ func newCSPAttributeSchema() schema.Attribute {
|
|||||||
"See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview/clouds) that Constellation supports.",
|
"See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview/clouds) that Constellation supports.",
|
||||||
Required: true,
|
Required: true,
|
||||||
Validators: []validator.String{
|
Validators: []validator.String{
|
||||||
stringvalidator.OneOf("aws", "azure", "gcp"),
|
stringvalidator.OneOf("aws", "azure", "gcp", "openstack", "stackit"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user