feat(openamt): add feature flag for OpenAMT [INT-5] (#6049)

feat(openamt): add feature flag for OpenAMT [INT-5]
pull/6064/head
andres-portainer 2021-11-11 15:49:50 -03:00 committed by GitHub
parent af0d637414
commit e126f63965
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 2 deletions

View File

@ -36,6 +36,7 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
Assets: kingpin.Flag("assets", "Path to the assets").Default(defaultAssetsDirectory).Short('a').String(),
Data: kingpin.Flag("data", "Path to the folder where the data is stored").Default(defaultDataDirectory).Short('d').String(),
EndpointURL: kingpin.Flag("host", "Environment URL").Short('H').String(),
FeatureFlags: boolPairs(kingpin.Flag("feat", "List of feature flags").Hidden()),
EnableEdgeComputeFeatures: kingpin.Flag("edge-compute", "Enable Edge Compute features").Bool(),
NoAnalytics: kingpin.Flag("no-analytics", "Disable Analytics in app (deprecated)").Bool(),
TLS: kingpin.Flag("tlsverify", "TLS support").Default(defaultTLS).Bool(),

View File

@ -1,11 +1,12 @@
package cli
import (
"github.com/portainer/portainer/api"
portainer "github.com/portainer/portainer/api"
"fmt"
"gopkg.in/alecthomas/kingpin.v2"
"strings"
"gopkg.in/alecthomas/kingpin.v2"
)
type pairList []portainer.Pair

45
api/cli/pairlistbool.go Normal file
View File

@ -0,0 +1,45 @@
package cli
import (
portainer "github.com/portainer/portainer/api"
"strings"
"gopkg.in/alecthomas/kingpin.v2"
)
type pairListBool []portainer.Pair
// Set implementation for a list of portainer.Pair
func (l *pairListBool) Set(value string) error {
p := new(portainer.Pair)
// default to true. example setting=true is equivalent to setting
parts := strings.SplitN(value, "=", 2)
if len(parts) != 2 {
p.Name = parts[0]
p.Value = "true"
} else {
p.Name = parts[0]
p.Value = parts[1]
}
*l = append(*l, *p)
return nil
}
// String implementation for a list of pair
func (l *pairListBool) String() string {
return ""
}
// IsCumulative implementation for a list of pair
func (l *pairListBool) IsCumulative() bool {
return true
}
func boolPairs(s kingpin.Settings) (target *[]portainer.Pair) {
target = new([]portainer.Pair)
s.SetValue((*pairListBool)(target))
return
}

View File

@ -2,8 +2,10 @@ package main
import (
"context"
"fmt"
"log"
"os"
"strconv"
"strings"
portainer "github.com/portainer/portainer/api"
@ -237,6 +239,49 @@ func updateSettingsFromFlags(dataStore portainer.DataStore, flags *portainer.CLI
return nil
}
// enableFeaturesFromFlags turns on or off feature flags
// e.g. portainer --feat open-amt --feat fdo=true ... (defaults to true)
// note, settins persisted to the DB. To turn off --feat open-amt=false
func enableFeaturesFromFlags(dataStore portainer.DataStore, flags *portainer.CLIFlags) error {
settings, err := dataStore.Settings().Settings()
if err != nil {
return err
}
if settings.FeatureFlagSettings == nil {
settings.FeatureFlagSettings = make(map[portainer.Feature]bool)
}
// loop through feature flags to check if they are supported
for _, feat := range *flags.FeatureFlags {
var correspondingFeature *portainer.Feature
for i, supportedFeat := range portainer.SupportedFeatureFlags {
if strings.EqualFold(feat.Name, string(supportedFeat)) {
correspondingFeature = &portainer.SupportedFeatureFlags[i]
}
}
if correspondingFeature == nil {
return fmt.Errorf("unknown feature flag '%s'", feat.Name)
}
featureState, err := strconv.ParseBool(feat.Value)
if err != nil {
return fmt.Errorf("feature flag's '%s' value should be true or false", feat.Name)
}
if featureState {
log.Printf("Feature %v : on", *correspondingFeature)
} else {
log.Printf("Feature %v : off", *correspondingFeature)
}
settings.FeatureFlagSettings[*correspondingFeature] = featureState
}
return dataStore.Settings().UpdateSettings(settings)
}
func loadAndParseKeyPair(fileService portainer.FileService, signatureService portainer.DigitalSignatureService) error {
private, public, err := fileService.LoadKeyPair()
if err != nil {
@ -492,6 +537,11 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
}
}
err = enableFeaturesFromFlags(dataStore, flags)
if err != nil {
log.Fatalf("failed enabling feature flag: %v", err)
}
err = edge.LoadEdgeJobs(dataStore, reverseTunnelService)
if err != nil {
log.Fatalf("failed loading edge jobs from database: %v", err)

View File

@ -16,6 +16,8 @@ type publicSettingsResponse struct {
AuthenticationMethod portainer.AuthenticationMethod `json:"AuthenticationMethod" example:"1"`
// Whether edge compute features are enabled
EnableEdgeComputeFeatures bool `json:"EnableEdgeComputeFeatures" example:"true"`
// Supported feature flags
Features map[portainer.Feature]bool `json:"Features"`
// The URL used for oauth login
OAuthLoginURI string `json:"OAuthLoginURI" example:"https://gitlab.com/oauth"`
// The URL used for oauth logout
@ -52,6 +54,7 @@ func generatePublicSettings(appSettings *portainer.Settings) *publicSettingsResp
EnableEdgeComputeFeatures: appSettings.EnableEdgeComputeFeatures,
EnableTelemetry: appSettings.EnableTelemetry,
KubeconfigExpiry: appSettings.KubeconfigExpiry,
Features: appSettings.FeatureFlagSettings,
}
//if OAuth authentication is on, compose the related fields from application settings
if publicSettings.AuthenticationMethod == portainer.AuthenticationOAuth {

View File

@ -50,6 +50,7 @@ type (
AdminPasswordFile *string
Assets *string
Data *string
FeatureFlags *[]Pair
EnableEdgeComputeFeatures *bool
EndpointURL *string
Labels *[]Pair
@ -388,6 +389,9 @@ type (
// ExtensionID represents a extension identifier
ExtensionID int
// Feature represents a feature that can be enabled or disabled via feature flags
Feature string
// GitlabRegistryData represents data required for gitlab registry to work
GitlabRegistryData struct {
ProjectID int `json:"ProjectId"`
@ -703,6 +707,7 @@ type (
AuthenticationMethod AuthenticationMethod `json:"AuthenticationMethod" example:"1"`
LDAPSettings LDAPSettings `json:"LDAPSettings" example:""`
OAuthSettings OAuthSettings `json:"OAuthSettings" example:""`
FeatureFlagSettings map[Feature]bool `json:"FeatureFlagSettings" example:""`
// The interval in which environment(endpoint) snapshots are created
SnapshotInterval string `json:"SnapshotInterval" example:"5m"`
// URL to the templates that will be displayed in the UI when navigating to App Templates
@ -1514,6 +1519,18 @@ const (
WebSocketKeepAlive = 1 * time.Hour
)
// Supported feature flags
const (
FeatOpenAMT Feature = "open-amt"
FeatFDO Feature = "fdo"
)
// List of supported features
var SupportedFeatureFlags = []Feature{
FeatOpenAMT,
FeatFDO,
}
const (
_ AuthenticationMethod = iota
// AuthenticationInternal represents the internal authentication method (authentication against Portainer API)

View File

@ -8,6 +8,7 @@ export function SettingsViewModel(data) {
this.TemplatesURL = data.TemplatesURL;
this.EdgeAgentCheckinInterval = data.EdgeAgentCheckinInterval;
this.EnableEdgeComputeFeatures = data.EnableEdgeComputeFeatures;
this.FeatureFlagSettings = data.FeatureFlagSettings;
this.UserSessionTimeout = data.UserSessionTimeout;
this.EnableTelemetry = data.EnableTelemetry;
this.KubeconfigExpiry = data.KubeconfigExpiry;
@ -17,6 +18,7 @@ export function SettingsViewModel(data) {
export function PublicSettingsViewModel(settings) {
this.AuthenticationMethod = settings.AuthenticationMethod;
this.EnableEdgeComputeFeatures = settings.EnableEdgeComputeFeatures;
this.FeatureFlagSettings = settings.FeatureFlagSettings;
this.LogoURL = settings.LogoURL;
this.OAuthLoginURI = settings.OAuthLoginURI;
this.EnableTelemetry = settings.EnableTelemetry;