mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
AB#2360 enterprise build tag (#397)
* enterprise build switch to disable license checking in default (OSS) version * remove community license quota * empty image references on OSS build in config Signed-off-by: Fabian Kammel <fk@edgeless.systems>
This commit is contained in:
parent
6b1c20792a
commit
45beec15f5
2
.github/actions/build_cli/action.yml
vendored
2
.github/actions/build_cli/action.yml
vendored
@ -51,7 +51,7 @@ runs:
|
||||
GIT_TAG=$(git describe --tags --always --dirty --abbrev=0)
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake -DCLI_VERSION:STRING=${GIT_TAG} ..
|
||||
cmake -DCLI_BUILD_TAGS:STRING=gcp,enterprise -DCLI_VERSION:STRING=${GIT_TAG} ..
|
||||
GOOS=${{ inputs.targetOS }} GOARCH=${{ inputs.targetArch }} make -j`nproc` cli
|
||||
cp constellation constellation-${{ inputs.targetOS }}-${{ inputs.targetArch }}
|
||||
echo "$(pwd)" >> $GITHUB_PATH
|
||||
|
@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.11)
|
||||
project(constellation LANGUAGES C VERSION 0.1.0)
|
||||
|
||||
set(CLI_VERSION "v0.1.0" CACHE STRING "Version of CLI binary.")
|
||||
set(CLI_BUILD_TAGS "gcp" CACHE STRING "Tags passed to go build of Constellation CLI.")
|
||||
|
||||
enable_testing()
|
||||
|
||||
@ -30,7 +31,7 @@ add_custom_target(bootstrapper ALL
|
||||
#
|
||||
|
||||
add_custom_target(cli ALL
|
||||
CGO_ENABLED=0 go build -o ${CMAKE_BINARY_DIR}/constellation -tags=gcp -ldflags "-buildid='' -X github.com/edgelesssys/constellation/internal/constants.VersionInfo=${CLI_VERSION}"
|
||||
CGO_ENABLED=0 go build -o ${CMAKE_BINARY_DIR}/constellation -tags='${CLI_BUILD_TAGS}' -ldflags "-buildid='' -X github.com/edgelesssys/constellation/internal/constants.VersionInfo=${CLI_VERSION}"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/cli
|
||||
BYPRODUCTS constellation
|
||||
)
|
||||
|
@ -65,7 +65,7 @@ func runInitialize(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// initialize initializes a Constellation.
|
||||
func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator) *dialer.Dialer,
|
||||
serviceAccCreator serviceAccountCreator, fileHandler file.Handler, helmLoader helmLoader, licenseClient licenseClient,
|
||||
serviceAccCreator serviceAccountCreator, fileHandler file.Handler, helmLoader helmLoader, quotaChecker license.QuotaChecker,
|
||||
) error {
|
||||
flags, err := evalFlagArgs(cmd, fileHandler)
|
||||
if err != nil {
|
||||
@ -87,22 +87,10 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator
|
||||
return fmt.Errorf("reading and validating config: %w", err)
|
||||
}
|
||||
|
||||
licenseID, err := license.FromFile(fileHandler, constants.LicenseFilename)
|
||||
if err != nil {
|
||||
cmd.Println("Unable to find license file. Assuming community license.")
|
||||
licenseID = license.CommunityLicense
|
||||
checker := license.NewChecker(quotaChecker, fileHandler)
|
||||
if err := checker.CheckLicense(cmd.Context(), cmd.Printf); err != nil {
|
||||
cmd.Printf("License check failed: %v", err)
|
||||
}
|
||||
quotaResp, err := licenseClient.CheckQuota(cmd.Context(), license.CheckQuotaRequest{
|
||||
License: licenseID,
|
||||
Action: license.Init,
|
||||
})
|
||||
if err != nil {
|
||||
cmd.Println("Unable to contact license server.")
|
||||
cmd.Println("Please keep your vCPU quota in mind.")
|
||||
cmd.Printf("For community installation the vCPU quota is: %d.\n", license.CommunityQuota)
|
||||
}
|
||||
cmd.Printf("Constellation license found: %s\n", licenseID)
|
||||
cmd.Printf("Please keep your vCPU quota (%d) in mind.\n", quotaResp.Quota)
|
||||
|
||||
var sshUsers []*ssh.UserKey
|
||||
for _, user := range config.SSHUsers {
|
||||
@ -414,7 +402,3 @@ func initCompletion(cmd *cobra.Command, args []string, toComplete string) ([]str
|
||||
type grpcDialer interface {
|
||||
Dial(ctx context.Context, target string) (*grpc.ClientConn, error)
|
||||
}
|
||||
|
||||
type licenseClient interface {
|
||||
CheckQuota(ctx context.Context, checkRequest license.CheckQuotaRequest) (license.CheckQuotaResponse, error)
|
||||
}
|
||||
|
@ -547,11 +547,13 @@ func defaultConfigWithExpectedMeasurements(t *testing.T, conf *config.Config, cs
|
||||
conf.Provider.Azure.TenantID = "01234567-0123-0123-0123-0123456789ab"
|
||||
conf.Provider.Azure.Location = "test-location"
|
||||
conf.Provider.Azure.UserAssignedIdentity = "test-identity"
|
||||
conf.Provider.Azure.Image = "some/image/location"
|
||||
conf.Provider.Azure.Measurements[8] = []byte("00000000000000000000000000000000")
|
||||
conf.Provider.Azure.Measurements[9] = []byte("11111111111111111111111111111111")
|
||||
case cloudprovider.GCP:
|
||||
conf.Provider.GCP.Region = "test-region"
|
||||
conf.Provider.GCP.Project = "test-project"
|
||||
conf.Provider.GCP.Image = "some/image/location"
|
||||
conf.Provider.GCP.Zone = "test-zone"
|
||||
conf.Provider.GCP.Measurements[8] = []byte("00000000000000000000000000000000")
|
||||
conf.Provider.GCP.Measurements[9] = []byte("11111111111111111111111111111111")
|
||||
@ -566,8 +568,8 @@ func defaultConfigWithExpectedMeasurements(t *testing.T, conf *config.Config, cs
|
||||
|
||||
type stubLicenseClient struct{}
|
||||
|
||||
func (c *stubLicenseClient) CheckQuota(ctx context.Context, checkRequest license.CheckQuotaRequest) (license.CheckQuotaResponse, error) {
|
||||
return license.CheckQuotaResponse{
|
||||
Quota: license.CommunityQuota,
|
||||
func (c *stubLicenseClient) QuotaCheck(ctx context.Context, checkRequest license.QuotaCheckRequest) (license.QuotaCheckResponse, error) {
|
||||
return license.QuotaCheckResponse{
|
||||
Quota: 25,
|
||||
}, nil
|
||||
}
|
||||
|
@ -226,7 +226,7 @@ func Default() *Config {
|
||||
TenantID: "",
|
||||
Location: "",
|
||||
UserAssignedIdentity: "",
|
||||
Image: "/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/CONSTELLATION-IMAGES/providers/Microsoft.Compute/galleries/Constellation/images/constellation/versions/1.5.0",
|
||||
Image: DefaultImageAzure,
|
||||
StateDiskType: "StandardSSD_LRS", // TODO: Replace with Premium_LRS when we replace the default VM size (Standard_D2a_v4) since the size does not support Premium_LRS
|
||||
Measurements: copyPCRMap(azurePCRs),
|
||||
EnforcedMeasurements: []uint32{8, 9, 11, 12},
|
||||
@ -235,7 +235,7 @@ func Default() *Config {
|
||||
Project: "",
|
||||
Region: "",
|
||||
Zone: "",
|
||||
Image: "projects/constellation-images/global/images/constellation-v1-5-0",
|
||||
Image: DefaultImageGCP,
|
||||
StateDiskType: "pd-ssd",
|
||||
ServiceAccountKeyPath: "serviceAccountKey.json",
|
||||
Measurements: copyPCRMap(gcpPCRs),
|
||||
|
@ -13,6 +13,8 @@ import (
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
const defaultMsgCount = 9 // expect this number of error messages by default because user-specific values are not set
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
goleak.VerifyTestMain(m)
|
||||
}
|
||||
@ -154,7 +156,7 @@ func TestValidate(t *testing.T) {
|
||||
}{
|
||||
"default config is valid": {
|
||||
cnf: Default(),
|
||||
wantMsgCount: 7, // expect 7 error messages by default because user-specific values are not set
|
||||
wantMsgCount: defaultMsgCount,
|
||||
},
|
||||
"config with 1 error": {
|
||||
cnf: func() *Config {
|
||||
@ -162,7 +164,7 @@ func TestValidate(t *testing.T) {
|
||||
cnf.Version = "v0"
|
||||
return cnf
|
||||
}(),
|
||||
wantMsgCount: 8,
|
||||
wantMsgCount: defaultMsgCount + 1,
|
||||
},
|
||||
"config with 2 errors": {
|
||||
cnf: func() *Config {
|
||||
@ -171,7 +173,7 @@ func TestValidate(t *testing.T) {
|
||||
cnf.StateDiskSizeGB = -1
|
||||
return cnf
|
||||
}(),
|
||||
wantMsgCount: 9,
|
||||
wantMsgCount: defaultMsgCount + 2,
|
||||
},
|
||||
}
|
||||
|
||||
|
8
internal/config/images_enterprise.go
Normal file
8
internal/config/images_enterprise.go
Normal file
@ -0,0 +1,8 @@
|
||||
//go:build enterprise
|
||||
|
||||
package config
|
||||
|
||||
const (
|
||||
DefaultImageAzure = "/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/CONSTELLATION-IMAGES/providers/Microsoft.Compute/galleries/Constellation/images/constellation/versions/1.5.0"
|
||||
DefaultImageGCP = "projects/constellation-images/global/images/constellation-v1-5-0"
|
||||
)
|
8
internal/config/images_oss.go
Normal file
8
internal/config/images_oss.go
Normal file
@ -0,0 +1,8 @@
|
||||
//go:build !enterprise
|
||||
|
||||
package config
|
||||
|
||||
const (
|
||||
DefaultImageAzure = ""
|
||||
DefaultImageGCP = ""
|
||||
)
|
46
internal/license/checker_enterprise.go
Normal file
46
internal/license/checker_enterprise.go
Normal file
@ -0,0 +1,46 @@
|
||||
//go:build enterprise
|
||||
|
||||
package license
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/edgelesssys/constellation/internal/constants"
|
||||
"github.com/edgelesssys/constellation/internal/file"
|
||||
)
|
||||
|
||||
type Checker struct {
|
||||
quotaChecker QuotaChecker
|
||||
fileHandler file.Handler
|
||||
}
|
||||
|
||||
func NewChecker(quotaChecker QuotaChecker, fileHandler file.Handler) *Checker {
|
||||
return &Checker{
|
||||
quotaChecker: quotaChecker,
|
||||
fileHandler: fileHandler,
|
||||
}
|
||||
}
|
||||
|
||||
// CheckLicense tries to read the license file and contact license server
|
||||
// to fetch quota information.
|
||||
// If no license file is found, community license is assumed.
|
||||
func (c *Checker) CheckLicense(ctx context.Context, printer func(string, ...any)) error {
|
||||
licenseID, err := FromFile(c.fileHandler, constants.LicenseFilename)
|
||||
if err != nil {
|
||||
printer("Unable to find license file. Assuming community license.\n")
|
||||
licenseID = CommunityLicense
|
||||
} else {
|
||||
printer("Constellation license found!\n")
|
||||
}
|
||||
quotaResp, err := c.quotaChecker.QuotaCheck(ctx, QuotaCheckRequest{
|
||||
License: licenseID,
|
||||
Action: Init,
|
||||
})
|
||||
if err != nil {
|
||||
printer("Unable to contact license server.\n")
|
||||
printer("Please keep your vCPU quota in mind.\n")
|
||||
} else {
|
||||
printer("Please keep your vCPU quota (%d) in mind.\n", quotaResp.Quota)
|
||||
}
|
||||
return nil
|
||||
}
|
20
internal/license/checker_oss.go
Normal file
20
internal/license/checker_oss.go
Normal file
@ -0,0 +1,20 @@
|
||||
//go:build !enterprise
|
||||
|
||||
package license
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/edgelesssys/constellation/internal/file"
|
||||
)
|
||||
|
||||
type Checker struct{}
|
||||
|
||||
func NewChecker(quotaChecker QuotaChecker, fileHandler file.Handler) *Checker {
|
||||
return &Checker{}
|
||||
}
|
||||
|
||||
// CheckLicense is a no-op for open source version of Constellation.
|
||||
func (c *Checker) CheckLicense(ctx context.Context, printer func(string, ...any)) error {
|
||||
return nil
|
||||
}
|
@ -12,10 +12,8 @@ import (
|
||||
const (
|
||||
// CommunityLicense is used by everyone who has not bought an enterprise license.
|
||||
CommunityLicense = "00000000-0000-0000-0000-000000000000"
|
||||
// CommunityQuota is the vCPU quota allowed for community installations of Constellation.
|
||||
CommunityQuota = 8
|
||||
apiHost = "license.confidential.cloud"
|
||||
licensePath = "api/v1/license"
|
||||
apiHost = "license.confidential.cloud"
|
||||
licensePath = "api/v1/license"
|
||||
)
|
||||
|
||||
type Action string
|
||||
@ -37,46 +35,46 @@ func NewClient() *Client {
|
||||
}
|
||||
}
|
||||
|
||||
// CheckQuotaRequest is JSON request to license server to check quota for a given license and action.
|
||||
type CheckQuotaRequest struct {
|
||||
// QuotaCheckRequest is JSON request to license server to check quota for a given license and action.
|
||||
type QuotaCheckRequest struct {
|
||||
Action Action `json:"action"`
|
||||
License string `json:"license"`
|
||||
}
|
||||
|
||||
// CheckQuotaResponse is JSON response by license server.
|
||||
type CheckQuotaResponse struct {
|
||||
// QuotaCheckResponse is JSON response by license server.
|
||||
type QuotaCheckResponse struct {
|
||||
Quota int `json:"quota"`
|
||||
}
|
||||
|
||||
// CheckQuota for a given license and action, passed via CheckQuotaRequest.
|
||||
func (c *Client) CheckQuota(ctx context.Context, checkRequest CheckQuotaRequest) (CheckQuotaResponse, error) {
|
||||
// QuotaCheck for a given license and action, passed via CheckQuotaRequest.
|
||||
func (c *Client) QuotaCheck(ctx context.Context, checkRequest QuotaCheckRequest) (QuotaCheckResponse, error) {
|
||||
reqBody, err := json.Marshal(checkRequest)
|
||||
if err != nil {
|
||||
return CheckQuotaResponse{}, fmt.Errorf("unable to marshal input: %w", err)
|
||||
return QuotaCheckResponse{}, fmt.Errorf("unable to marshal input: %w", err)
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, licenseURL().String(), bytes.NewBuffer(reqBody))
|
||||
if err != nil {
|
||||
return CheckQuotaResponse{}, fmt.Errorf("unable to create request: %w", err)
|
||||
return QuotaCheckResponse{}, fmt.Errorf("unable to create request: %w", err)
|
||||
}
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return CheckQuotaResponse{}, fmt.Errorf("unable to do request: %w", err)
|
||||
return QuotaCheckResponse{}, fmt.Errorf("unable to do request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return CheckQuotaResponse{}, fmt.Errorf("http error %d", resp.StatusCode)
|
||||
return QuotaCheckResponse{}, fmt.Errorf("http error %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
responseContentType := resp.Header.Get("Content-Type")
|
||||
if responseContentType != "application/json" {
|
||||
return CheckQuotaResponse{}, fmt.Errorf("expected server JSON response but got '%s'", responseContentType)
|
||||
return QuotaCheckResponse{}, fmt.Errorf("expected server JSON response but got '%s'", responseContentType)
|
||||
}
|
||||
|
||||
var parsedResponse CheckQuotaResponse
|
||||
var parsedResponse QuotaCheckResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&parsedResponse)
|
||||
if err != nil {
|
||||
return CheckQuotaResponse{}, fmt.Errorf("unable to parse response: %w", err)
|
||||
return QuotaCheckResponse{}, fmt.Errorf("unable to parse response: %w", err)
|
||||
}
|
||||
|
||||
return parsedResponse, nil
|
||||
@ -89,3 +87,7 @@ func licenseURL() *url.URL {
|
||||
Path: licensePath,
|
||||
}
|
||||
}
|
||||
|
||||
type QuotaChecker interface {
|
||||
QuotaCheck(ctx context.Context, checkRequest QuotaCheckRequest) (QuotaCheckResponse, error)
|
||||
}
|
||||
|
@ -9,18 +9,13 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCheckQuotaIntegration(t *testing.T) {
|
||||
func TestQuotaCheckIntegration(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
license string
|
||||
action Action
|
||||
wantQuota int
|
||||
wantError bool
|
||||
}{
|
||||
"ES license has quota 256": {
|
||||
license: "***REMOVED***",
|
||||
action: test,
|
||||
wantQuota: 256,
|
||||
},
|
||||
"OSS license has quota 8": {
|
||||
license: CommunityLicense,
|
||||
action: test,
|
||||
@ -49,11 +44,11 @@ func TestCheckQuotaIntegration(t *testing.T) {
|
||||
|
||||
client := NewClient()
|
||||
|
||||
req := CheckQuotaRequest{
|
||||
req := QuotaCheckRequest{
|
||||
Action: tc.action,
|
||||
License: tc.license,
|
||||
}
|
||||
resp, err := client.CheckQuota(context.Background(), req)
|
||||
resp, err := client.QuotaCheck(context.Background(), req)
|
||||
|
||||
if tc.wantError {
|
||||
assert.Error(err)
|
||||
|
@ -27,7 +27,7 @@ func newTestClient(fn roundTripFunc) *Client {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckQuota(t *testing.T) {
|
||||
func TestQuotaCheck(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
license string
|
||||
serverResponse string
|
||||
@ -37,7 +37,7 @@ func TestCheckQuota(t *testing.T) {
|
||||
wantError bool
|
||||
}{
|
||||
"success": {
|
||||
license: "***REMOVED***",
|
||||
license: "0c0a6558-f8af-4063-bf61-92e7ac4cb052",
|
||||
serverResponse: "{\"quota\":256}",
|
||||
serverResponseCode: http.StatusOK,
|
||||
serverResponseContent: "application/json",
|
||||
@ -74,7 +74,7 @@ func TestCheckQuota(t *testing.T) {
|
||||
return r
|
||||
})
|
||||
|
||||
resp, err := client.CheckQuota(context.Background(), CheckQuotaRequest{
|
||||
resp, err := client.QuotaCheck(context.Background(), QuotaCheckRequest{
|
||||
Action: test,
|
||||
License: tc.license,
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user