diff --git a/client/src/app/ctl/config/config-init/config-init.component.css b/client/src/app/ctl/config/config-init/config-init.component.css new file mode 100644 index 0000000..f369d7e --- /dev/null +++ b/client/src/app/ctl/config/config-init/config-init.component.css @@ -0,0 +1,31 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +.grey-icon { + color: grey; +} + +.mat-action-row { + place-content: space-between; +} + +.title { + color: #3f51b5; +} + +mat-form-field { + width: 80%; + padding-left: 15px; + padding-right: 15px; +} diff --git a/client/src/app/ctl/config/config-init/config-init.component.html b/client/src/app/ctl/config/config-init/config-init.component.html new file mode 100644 index 0000000..4185272 --- /dev/null +++ b/client/src/app/ctl/config/config-init/config-init.component.html @@ -0,0 +1,23 @@ +
+

Initialize new Airship configuration file

+ + Path + + + + +
+ +
+

Specify existing Airship configuration file

+ + Path + + + + +
diff --git a/client/src/app/ctl/config/config-init/config-init.component.spec.ts b/client/src/app/ctl/config/config-init/config-init.component.spec.ts new file mode 100644 index 0000000..ba91f75 --- /dev/null +++ b/client/src/app/ctl/config/config-init/config-init.component.spec.ts @@ -0,0 +1,61 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrModule } from 'ngx-toastr'; + +import { ConfigInitComponent } from './config-init.component'; + +describe('ConfigEncryptionComponent', () => { + let component: ConfigInitComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ConfigInitComponent ], + imports: [ + BrowserAnimationsModule, + FormsModule, + MatInputModule, + MatIconModule, + MatCheckboxModule, + MatButtonModule, + ReactiveFormsModule, + ToastrModule.forRoot(), + MatExpansionModule, + MatDividerModule + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ConfigInitComponent); + component = fixture.componentInstance; + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/ctl/config/config-init/config-init.component.ts b/client/src/app/ctl/config/config-init/config-init.component.ts new file mode 100644 index 0000000..bd6c301 --- /dev/null +++ b/client/src/app/ctl/config/config-init/config-init.component.ts @@ -0,0 +1,46 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { Component } from '@angular/core'; +import { FormControl } from '@angular/forms'; +import { WebsocketService } from '../../../../services/websocket/websocket.service'; +import { WebsocketMessage } from '../../../../services/websocket/websocket.models'; + +@Component({ + selector: 'app-config-init', + templateUrl: './config-init.component.html', + styleUrls: ['./config-init.component.css'] +}) +export class ConfigInitComponent { + type = 'ctl'; + component = 'config'; + + initValue = new FormControl(''); + specifyValue = new FormControl(''); + + constructor(private websocketService: WebsocketService) {} + + initAirshipConfig(): void { + const msg = new WebsocketMessage(this.type, this.component, 'init'); + msg.message = this.initValue.value; + this.websocketService.sendMessage(msg); + } + + setAirshipConfig(): void { + const msg = new WebsocketMessage(this.type, this.component, 'setAirshipConfig'); + msg.message = this.specifyValue.value; + this.websocketService.sendMessage(msg); + } + +} diff --git a/client/src/app/ctl/config/config-init/config-init.module.ts b/client/src/app/ctl/config/config-init/config-init.module.ts new file mode 100644 index 0000000..5244e17 --- /dev/null +++ b/client/src/app/ctl/config/config-init/config-init.module.ts @@ -0,0 +1,35 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatInputModule } from '@angular/material/input'; +import { MatCardModule } from '@angular/material/card'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + MatInputModule, + MatCardModule, + MatButtonModule, + ReactiveFormsModule, + ], + declarations: [ + ], + providers: [] + }) + export class ConfigInitModule { } diff --git a/client/src/app/ctl/config/config.component.html b/client/src/app/ctl/config/config.component.html index 8e56cbc..2e9a839 100755 --- a/client/src/app/ctl/config/config.component.html +++ b/client/src/app/ctl/config/config.component.html @@ -1,6 +1,7 @@

Airship Configuration Options

+
Airship Config File: {{airshipConfigPath}}
-
+
@@ -45,3 +46,8 @@
+ +
+ +
+
diff --git a/client/src/app/ctl/config/config.component.ts b/client/src/app/ctl/config/config.component.ts index 3dbc12d..43c3c3e 100755 --- a/client/src/app/ctl/config/config.component.ts +++ b/client/src/app/ctl/config/config.component.ts @@ -30,6 +30,8 @@ export class ConfigComponent implements WSReceiver, OnInit { type = 'ctl'; component = 'config'; + airshipConfigPath: string; + currentContext: string; contexts: Context[] = []; manifests: Manifest[] = []; @@ -49,6 +51,17 @@ export class ConfigComponent implements WSReceiver, OnInit { this.websocketService.printIfToast(message); } else { switch (message.subComponent) { + case 'init': + this.websocketService.printIfToast(message); + this.getConfig(); + break; + case 'setAirshipConfig': + this.websocketService.printIfToast(message); + this.getConfig(); + break; + case 'getAirshipConfigPath': + this.airshipConfigPath = message.message; + break; case 'getCurrentContext': this.currentContext = message.message; break; @@ -87,6 +100,7 @@ export class ConfigComponent implements WSReceiver, OnInit { } getConfig(): void { + this.getAirshipConfigPath(); this.getCurrentContext(); this.getContexts(); this.getManifests(); @@ -94,6 +108,12 @@ export class ConfigComponent implements WSReceiver, OnInit { this.getEncryptionConfigs(); } + getAirshipConfigPath(): void { + this.websocketService.sendMessage(new WebsocketMessage( + this.type, this.component, 'getAirshipConfigPath') + ); + } + getCurrentContext(): void { this.websocketService.sendMessage(new WebsocketMessage( this.type, this.component, 'getCurrentContext') diff --git a/client/src/app/ctl/config/config.module.ts b/client/src/app/ctl/config/config.module.ts index 03b0d4e..3db3792 100755 --- a/client/src/app/ctl/config/config.module.ts +++ b/client/src/app/ctl/config/config.module.ts @@ -25,6 +25,7 @@ import { ConfigContextComponent } from './config-context/config-context.componen import { ConfigEncryptionComponent } from './config-encryption/config-encryption.component'; import { ConfigManagementComponent } from './config-management/config-management.component'; import { ConfigManifestComponent } from './config-manifest/config-manifest.component'; +import { ConfigInitComponent } from './config-init/config-init.component'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatExpansionModule } from '@angular/material/expansion'; @@ -46,7 +47,8 @@ import { MatExpansionModule } from '@angular/material/expansion'; ConfigContextComponent, ConfigEncryptionComponent, ConfigManagementComponent, - ConfigManifestComponent + ConfigManifestComponent, + ConfigInitComponent ], providers: [], schemas: [CUSTOM_ELEMENTS_SCHEMA] diff --git a/client/src/app/ctl/ctl.module.ts b/client/src/app/ctl/ctl.module.ts index 287dfb4..7723625 100644 --- a/client/src/app/ctl/ctl.module.ts +++ b/client/src/app/ctl/ctl.module.ts @@ -22,9 +22,11 @@ import { CtlRoutingModule } from './ctl-routing.module'; import { PhaseModule } from './phase/phase.module'; import { SecretModule } from './secret/secret.module'; import { ConfigModule } from './config/config.module'; +import { CommonModule } from '@angular/common'; @NgModule({ imports: [ + CommonModule, CtlRoutingModule, ClusterModule, ConfigModule, diff --git a/go.sum b/go.sum index 356d120..cb10933 100644 --- a/go.sum +++ b/go.sum @@ -125,6 +125,7 @@ github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -702,6 +703,7 @@ github.com/yujunz/go-getter v1.4.1-lite h1:FhvNc94AXMZkfqUwfMKhnQEC9phkphSGdPTL7 github.com/yujunz/go-getter v1.4.1-lite/go.mod h1:sbmqxXjyLunH1PkF3n7zSlnVeMvmYUuIl9ZVs/7NyCc= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= diff --git a/pkg/configs/configs.go b/pkg/configs/configs.go index 2f75e37..14c64cc 100644 --- a/pkg/configs/configs.go +++ b/pkg/configs/configs.go @@ -137,6 +137,8 @@ const ( Status WsSubComponentType = "status" // ctl config subcomponets + SetAirshipConfig WsSubComponentType = "setAirshipConfig" + GetAirshipConfigPath WsSubComponentType = "getAirshipConfigPath" GetContexts WsSubComponentType = "getContexts" GetCurrentContext WsSubComponentType = "getCurrentContext" GetEncryptionConfigs WsSubComponentType = "getEncryptionConfigs" diff --git a/pkg/ctl/airshipctl.go b/pkg/ctl/airshipctl.go index 560b2b5..879310c 100644 --- a/pkg/ctl/airshipctl.go +++ b/pkg/ctl/airshipctl.go @@ -15,6 +15,9 @@ package ctl import ( + "fmt" + "os" + "opendev.org/airship/airshipctl/pkg/config" "opendev.org/airship/airshipctl/pkg/log" "opendev.org/airship/airshipui/pkg/configs" @@ -31,6 +34,11 @@ var AirshipConfigPath *string // KubeConfigPath location of kubeconfig used by airshipctl (default $HOME/.airship/kubeconfig) var KubeConfigPath *string +const ( + AirshipConfigNotFoundErr = `No airship config file found. + Please visit the Config section to specify or initialize a config file.` +) + // CTLFunctionMap is a function map for the CTL functions that is referenced in the webservice var CTLFunctionMap = map[configs.WsComponentType]func(*string, configs.WsMessage) configs.WsMessage{ configs.Baremetal: HandleBaremetalRequest, @@ -63,8 +71,25 @@ func Init() { webservice.AppendToFunctionMap(configs.CTL, CTLFunctionMap) } +func configFileExists(airshipConfigPath *string) bool { + if airshipConfigPath == nil { + return false + } + + info, err := os.Stat(*airshipConfigPath) + if os.IsNotExist(err) { + return false + } + + return !info.IsDir() +} + // NewDefaultClient initializes the airshipctl client for external usage with default logging. func NewDefaultClient(airshipConfigPath *string) (*Client, error) { + if !configFileExists(airshipConfigPath) { + return nil, fmt.Errorf(AirshipConfigNotFoundErr) + } + cfgFactory := config.CreateFactory(airshipConfigPath) // TODO(mfuller): Factory doesn't throw an error if there's no diff --git a/pkg/ctl/config.go b/pkg/ctl/config.go index c7b9a7d..94725d6 100644 --- a/pkg/ctl/config.go +++ b/pkg/ctl/config.go @@ -17,82 +17,124 @@ package ctl import ( "encoding/json" "fmt" + "os" + "path/filepath" ctlconfig "opendev.org/airship/airshipctl/pkg/config" "opendev.org/airship/airshipui/pkg/configs" - "opendev.org/airship/airshipui/pkg/log" ) -// HandleConfigRequest will flop between requests so we don't have to have them all mapped as function calls -// This will wait for the sub component to complete before responding. The assumption is this is an async request -func HandleConfigRequest(user *string, request configs.WsMessage) configs.WsMessage { - response := configs.WsMessage{ +// ConfigFunctionMap is being used to call the appropriate function based on the SubComponentType, +// since the linter seems to think there are too many cases for a switch / case +var ConfigFunctionMap = map[configs.WsSubComponentType]func(configs.WsMessage) configs.WsMessage{ + configs.SetAirshipConfig: SetAirshipConfig, + configs.GetAirshipConfigPath: GetAirshipConfigPath, + configs.GetCurrentContext: GetCurrentContext, + configs.GetContexts: GetContexts, + configs.GetEncryptionConfigs: GetEncryptionConfigs, + configs.GetManagementConfigs: GetManagementConfigs, + configs.GetManifests: GetManifests, + configs.Init: InitAirshipConfig, + configs.SetContext: SetContext, + configs.SetEncryptionConfig: SetEncryptionConfig, + configs.SetManagementConfig: SetManagementConfig, + configs.SetManifest: SetManifest, + configs.UseContext: UseContext, +} + +// helper function to create most of the relevant bits of the response message +func newResponse(request configs.WsMessage) configs.WsMessage { + return configs.WsMessage{ Type: configs.CTL, Component: configs.CTLConfig, SubComponent: request.SubComponent, Name: request.Name, } +} - var err error - var message *string +// HandleConfigRequest will flop between requests so we don't have to have them all mapped as function calls +// This will wait for the sub component to complete before responding. The assumption is this is an async request +func HandleConfigRequest(user *string, request configs.WsMessage) configs.WsMessage { + var response configs.WsMessage - client, err := NewClient(AirshipConfigPath, KubeConfigPath, request) - if err != nil { - e := fmt.Sprintf("Error initializing airshipctl client: %s", err) - response.Error = &e - return response - } - - subComponent := request.SubComponent - switch subComponent { - case configs.GetCurrentContext: - context := client.Config.CurrentContext - message = &context - case configs.GetContexts: - response.Data = GetContexts(client) - case configs.GetEncryptionConfigs: - response.Data = GetEncryptionConfigs(client) - case configs.GetManagementConfigs: - response.Data = GetManagementConfigs(client) - case configs.GetManifests: - response.Data = GetManifests(client) - case configs.Init: - err = InitAirshipConfig(AirshipConfigPath) - case configs.SetContext: - response.Data, err = SetContext(client, request) - str := fmt.Sprintf("Context '%s' has been modified", request.Name) - message = &str - case configs.SetEncryptionConfig: - response.Data, err = SetEncryptionConfig(client, request) - str := fmt.Sprintf("Encryption configuration '%s' has been modified", request.Name) - message = &str - case configs.SetManagementConfig: - err = SetManagementConfig(client, request) - str := fmt.Sprintf("Management configuration '%s' has been modified", request.Name) - message = &str - case configs.SetManifest: - response.Data, err = SetManifest(client, request) - str := fmt.Sprintf("Manifest '%s' has been modified", request.Name) - message = &str - case configs.UseContext: - err = UseContext(client, request) - default: - err = fmt.Errorf("Subcomponent %s not found", request.SubComponent) - } - - if err != nil { - e := err.Error() - response.Error = &e + if handler, ok := ConfigFunctionMap[request.SubComponent]; ok { + response = handler(request) } else { - response.Message = message + response = newResponse(request) + err := fmt.Sprintf("Subcomponent %s not found", request.SubComponent) + response.Error = &err } return response } +// GetAirshipConfigPath returns value stored in AirshipConfigPath +func GetAirshipConfigPath(request configs.WsMessage) configs.WsMessage { + response := newResponse(request) + response.Message = AirshipConfigPath + return response +} + +// SetAirshipConfig sets the AirshipConfigPath to the value specified by +// UI client +func SetAirshipConfig(request configs.WsMessage) configs.WsMessage { + response := newResponse(request) + + AirshipConfigPath = request.Message + + msg := fmt.Sprintf("Config file set to '%s'", *AirshipConfigPath) + response.Message = &msg + + return response +} + +// GetCurrentContext returns the name of the currently configured context +func GetCurrentContext(request configs.WsMessage) configs.WsMessage { + response := newResponse(request) + + client, err := NewClient(AirshipConfigPath, KubeConfigPath, request) + if err != nil { + e := err.Error() + response.Error = &e + return response + } + + response.Message = &client.Config.CurrentContext + + return response +} + // InitAirshipConfig wrapper function for CTL's CreateConfig using the specified path -func InitAirshipConfig(path *string) error { - return ctlconfig.CreateConfig(*path) +// TODO(mfuller): we'll need to persist this info in airshipui.json so that we can +// set AirshipConfigPath at app launch +func InitAirshipConfig(request configs.WsMessage) configs.WsMessage { + response := newResponse(request) + + confPath := *request.Message + if confPath == "" { + home, err := os.UserHomeDir() + if err != nil { + e := err.Error() + response.Error = &e + return response + } + confPath = filepath.Join(home, ".airship", "config") + } + + err := ctlconfig.CreateConfig(confPath) + if err != nil { + e := err.Error() + response.Error = &e + return response + } + + AirshipConfigPath = &confPath + + msg := fmt.Sprintf("Config file set to '%s'", *AirshipConfigPath) + + response.Message = &msg + + return response } // Context wrapper struct to include context name with CTL's Context @@ -103,7 +145,16 @@ type Context struct { // GetContexts returns a slice of wrapper Context structs so we know the name of each // for display in the UI -func GetContexts(client *Client) []Context { +func GetContexts(request configs.WsMessage) configs.WsMessage { + response := newResponse(request) + + client, err := NewClient(AirshipConfigPath, KubeConfigPath, request) + if err != nil { + e := err.Error() + response.Error = &e + return response + } + contexts := []Context{} for name, context := range client.Config.Contexts { contexts = append(contexts, Context{ @@ -117,7 +168,9 @@ func GetContexts(client *Client) []Context { }) } - return contexts + response.Data = contexts + + return response } // Manifest wraps CTL's Manifest to include the manifest name @@ -128,7 +181,16 @@ type Manifest struct { // GetManifests returns a slice of wrapper Manifest structs so we know the name of each // for display in the UI -func GetManifests(client *Client) []Manifest { +func GetManifests(request configs.WsMessage) configs.WsMessage { + response := newResponse(request) + + client, err := NewClient(AirshipConfigPath, KubeConfigPath, request) + if err != nil { + e := err.Error() + response.Error = &e + return response + } + manifests := []Manifest{} for name, manifest := range client.Config.Manifests { @@ -138,7 +200,9 @@ func GetManifests(client *Client) []Manifest { }) } - return manifests + response.Data = manifests + + return response } // ManagementConfig wrapper struct for CTL's ManagementConfiguration that @@ -149,7 +213,16 @@ type ManagementConfig struct { } // GetManagementConfigs function to retrieve all management configs -func GetManagementConfigs(client *Client) []ManagementConfig { +func GetManagementConfigs(request configs.WsMessage) configs.WsMessage { + response := newResponse(request) + + client, err := NewClient(AirshipConfigPath, KubeConfigPath, request) + if err != nil { + e := err.Error() + response.Error = &e + return response + } + configs := []ManagementConfig{} for name, conf := range client.Config.ManagementConfiguration { configs = append(configs, ManagementConfig{ @@ -163,7 +236,10 @@ func GetManagementConfigs(client *Client) []ManagementConfig { }, }) } - return configs + + response.Data = configs + + return response } // EncryptionConfig wrapper struct for CTL's EncryptionConfiguration that @@ -175,7 +251,16 @@ type EncryptionConfig struct { // GetEncryptionConfigs returns a slice of wrapper EncryptionConfig structs so we // know the name of each for display in the UI -func GetEncryptionConfigs(client *Client) []EncryptionConfig { +func GetEncryptionConfigs(request configs.WsMessage) configs.WsMessage { + response := newResponse(request) + + client, err := NewClient(AirshipConfigPath, KubeConfigPath, request) + if err != nil { + e := err.Error() + response.Error = &e + return response + } + configs := []EncryptionConfig{} for name, config := range client.Config.EncryptionConfigs { configs = append(configs, EncryptionConfig{ @@ -187,101 +272,216 @@ func GetEncryptionConfigs(client *Client) []EncryptionConfig { }) } - return configs + response.Data = configs + + return response } // SetContext wrapper function for CTL's RunSetContext, using a UI client -func SetContext(client *Client, message configs.WsMessage) (bool, error) { - bytes, err := json.Marshal(message.Data) +func SetContext(request configs.WsMessage) configs.WsMessage { + response := newResponse(request) + + client, err := NewClient(AirshipConfigPath, KubeConfigPath, request) if err != nil { - return false, err + e := err.Error() + response.Error = &e + return response + } + + bytes, err := json.Marshal(request.Data) + if err != nil { + e := err.Error() + response.Error = &e + return response } var opts ctlconfig.ContextOptions err = json.Unmarshal(bytes, &opts) if err != nil { - return false, err + e := err.Error() + response.Error = &e + return response } err = opts.Validate() if err != nil { - return false, err + e := err.Error() + response.Error = &e + return response } - return ctlconfig.RunSetContext(&opts, client.Config, true) + _, err = ctlconfig.RunSetContext(&opts, client.Config, true) + if err != nil { + e := err.Error() + response.Error = &e + return response + } + + msg := fmt.Sprintf("Context '%s' has been modified", request.Name) + response.Message = &msg + + return response } // SetEncryptionConfig wrapper function for CTL's RunSetEncryptionConfig, using a UI client -func SetEncryptionConfig(client *Client, message configs.WsMessage) (bool, error) { - bytes, err := json.Marshal(message.Data) +func SetEncryptionConfig(request configs.WsMessage) configs.WsMessage { + response := newResponse(request) + + client, err := NewClient(AirshipConfigPath, KubeConfigPath, request) if err != nil { - return false, err + e := err.Error() + response.Error = &e + return response + } + + bytes, err := json.Marshal(request.Data) + if err != nil { + e := err.Error() + response.Error = &e + return response } var opts ctlconfig.EncryptionConfigOptions err = json.Unmarshal(bytes, &opts) if err != nil { - return false, err + e := err.Error() + response.Error = &e + return response } err = opts.Validate() if err != nil { - return false, err + e := err.Error() + response.Error = &e + return response } - return ctlconfig.RunSetEncryptionConfig(&opts, client.Config, true) + _, err = ctlconfig.RunSetEncryptionConfig(&opts, client.Config, true) + if err != nil { + e := err.Error() + response.Error = &e + return response + } + + msg := fmt.Sprintf("Encryption configuration '%s' has been modified", request.Name) + response.Message = &msg + + return response } // SetManagementConfig sets the specified management configuration with values // received from the frontend client // TODO(mfuller): there's currently no setter for this in the CTL config pkg // so we'll set the values manually and then persist the config -func SetManagementConfig(client *Client, message configs.WsMessage) error { - bytes, err := json.Marshal(message.Data) +func SetManagementConfig(request configs.WsMessage) configs.WsMessage { + response := newResponse(request) + + client, err := NewClient(AirshipConfigPath, KubeConfigPath, request) if err != nil { - return err + e := err.Error() + response.Error = &e + return response } - if mCfg, found := client.Config.ManagementConfiguration[message.Name]; found { + bytes, err := json.Marshal(request.Data) + if err != nil { + e := err.Error() + response.Error = &e + return response + } + + if mCfg, found := client.Config.ManagementConfiguration[request.Name]; found { err = json.Unmarshal(bytes, mCfg) if err != nil { - return err + e := err.Error() + response.Error = &e + return response } err = client.Config.PersistConfig() if err != nil { - return err + e := err.Error() + response.Error = &e + return response } } else { - return fmt.Errorf("Management configuration '%s' not found", message.Name) + e := fmt.Sprintf("Management configuration '%s' not found", request.Name) + response.Error = &e + return response } - return nil + msg := fmt.Sprintf("Management configuration '%s' has been modified", request.Name) + response.Message = &msg + + return response } // SetManifest wrapper function for CTL's RunSetManifest, using a UI client -func SetManifest(client *Client, message configs.WsMessage) (bool, error) { - bytes, err := json.Marshal(message.Data) +func SetManifest(request configs.WsMessage) configs.WsMessage { + response := newResponse(request) + + client, err := NewClient(AirshipConfigPath, KubeConfigPath, request) if err != nil { - return false, err + e := err.Error() + response.Error = &e + return response + } + + bytes, err := json.Marshal(request.Data) + if err != nil { + e := err.Error() + response.Error = &e + return response } var opts ctlconfig.ManifestOptions err = json.Unmarshal(bytes, &opts) if err != nil { - return false, err + e := err.Error() + response.Error = &e + return response } - log.Infof("Unmarshaled options: %+v", opts) err = opts.Validate() if err != nil { - return false, err + e := err.Error() + response.Error = &e + return response } - return ctlconfig.RunSetManifest(&opts, client.Config, true) + _, err = ctlconfig.RunSetManifest(&opts, client.Config, true) + if err != nil { + e := err.Error() + response.Error = &e + return response + } + + msg := fmt.Sprintf("Manifest '%s' has been modified", request.Name) + response.Message = &msg + + return response } // UseContext wrapper function for CTL's RunUseConfig, using a UI client -func UseContext(client *Client, message configs.WsMessage) error { - return ctlconfig.RunUseContext(message.Name, client.Config) +func UseContext(request configs.WsMessage) configs.WsMessage { + response := newResponse(request) + + client, err := NewClient(AirshipConfigPath, KubeConfigPath, request) + if err != nil { + e := err.Error() + response.Error = &e + return response + } + + err = ctlconfig.RunUseContext(request.Name, client.Config) + if err != nil { + e := err.Error() + response.Error = &e + return response + } + + msg := fmt.Sprintf("Using context '%s'", request.Name) + response.Message = &msg + + return response }