From a4805278082aaade479b68498c40165913a9a163 Mon Sep 17 00:00:00 2001
From: Rodolfo Pacheco <jezogwza@gmail.com>
Date: Wed, 4 Dec 2019 17:19:06 -0500
Subject: [PATCH] Introduces set_context and get_context operations Each of
 these include an option for --current-context that set or retrieves the
 curret context

This patchset mainly creates the cmd/config and pkg/config require additions

Also includes a fync getCurrentContext(<CLUSTERTYPE>)  in the config pkg
that other modules should rely on.

Introduces new  ErrMissingConfig and  ErrConfigFailed types been used by
set-context, will decimate through get/ and set/get cluster after this is
reviewed.

Change-Id: I501483a9db99f33f860eaf329a65bb0209b2aaff
---
 cmd/config/config.go                          |   2 +
 cmd/config/get_cluster.go                     |   6 +-
 cmd/config/get_context.go                     | 107 ++++++++++
 cmd/config/get_context_test.go                | 116 +++++++++++
 cmd/config/set_context.go                     | 141 +++++++++++++
 cmd/config/set_context_test.go                | 190 ++++++++++++++++++
 .../config-cmd-with-defaults.golden           |   4 +-
 .../config-cmd-with-help.golden               |   4 +-
 .../config-cmd-set-context-with-help.golden   |  23 +++
 .../get-all-contexts.golden                   |  27 +++
 .../get-context.golden                        |   9 +
 .../get-current-context.golden                |   9 +
 .../get-multiple-contexts.golden              |  27 +++
 .../missing.golden                            |  18 ++
 .../no-contexts.golden                        |   0
 pkg/config/cmds.go                            |  15 ++
 pkg/config/cmds_test.go                       |  10 +-
 pkg/config/cmds_types.go                      |  10 +
 pkg/config/config.go                          | 165 ++++++++++++++-
 pkg/config/config_test.go                     |  35 +++-
 pkg/config/constants.go                       |  21 +-
 pkg/config/test_utils.go                      |  17 ++
 pkg/config/testdata/context-string.yaml       |   5 +
 pkg/config/types.go                           |   9 +-
 pkg/errors/common.go                          |  33 ++-
 25 files changed, 968 insertions(+), 35 deletions(-)
 create mode 100644 cmd/config/get_context.go
 create mode 100644 cmd/config/get_context_test.go
 create mode 100644 cmd/config/set_context.go
 create mode 100644 cmd/config/set_context_test.go
 create mode 100644 cmd/config/testdata/TestConfigSetContextGoldenOutput/config-cmd-set-context-with-help.golden
 create mode 100644 cmd/config/testdata/TestGetContextCmdGoldenOutput/get-all-contexts.golden
 create mode 100644 cmd/config/testdata/TestGetContextCmdGoldenOutput/get-context.golden
 create mode 100644 cmd/config/testdata/TestGetContextCmdGoldenOutput/get-current-context.golden
 create mode 100644 cmd/config/testdata/TestGetContextCmdGoldenOutput/get-multiple-contexts.golden
 create mode 100644 cmd/config/testdata/TestGetContextCmdGoldenOutput/missing.golden
 create mode 100644 cmd/config/testdata/TestNoContextsGetContextCmdGoldenOutput/no-contexts.golden

diff --git a/cmd/config/config.go b/cmd/config/config.go
index 4ddc5f8c3..49e9c86b7 100644
--- a/cmd/config/config.go
+++ b/cmd/config/config.go
@@ -17,6 +17,8 @@ like "airshipctl config set-current-context my-context" `),
 	}
 	configRootCmd.AddCommand(NewCmdConfigSetCluster(rootSettings))
 	configRootCmd.AddCommand(NewCmdConfigGetCluster(rootSettings))
+	configRootCmd.AddCommand(NewCmdConfigSetContext(rootSettings))
+	configRootCmd.AddCommand(NewCmdConfigGetContext(rootSettings))
 
 	return configRootCmd
 }
diff --git a/cmd/config/get_cluster.go b/cmd/config/get_cluster.go
index b04fd501b..a0c7e0954 100644
--- a/cmd/config/get_cluster.go
+++ b/cmd/config/get_cluster.go
@@ -27,8 +27,7 @@ import (
 )
 
 var (
-	getClusterLong = (`
-Gets a specific cluster or all defined clusters if no name is provided`)
+	getClusterLong = (`Display a specific cluster or all defined clusters if no name is provided`)
 
 	getClusterExample = fmt.Sprintf(`
 # List all the clusters airshipctl knows about
@@ -44,8 +43,7 @@ func NewCmdConfigGetCluster(rootSettings *environment.AirshipCTLSettings) *cobra
 	theCluster := &config.ClusterOptions{}
 	getclustercmd := &cobra.Command{
 		Use:     "get-cluster NAME",
-		Short:   "Display a specific cluster",
-		Long:    getClusterLong,
+		Short:   getClusterLong,
 		Example: getClusterExample,
 		RunE: func(cmd *cobra.Command, args []string) error {
 			if len(args) == 1 {
diff --git a/cmd/config/get_context.go b/cmd/config/get_context.go
new file mode 100644
index 000000000..b77048457
--- /dev/null
+++ b/cmd/config/get_context.go
@@ -0,0 +1,107 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+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.
+*/
+
+package config
+
+import (
+	"fmt"
+	"io"
+
+	"github.com/spf13/cobra"
+
+	"opendev.org/airship/airshipctl/pkg/config"
+	"opendev.org/airship/airshipctl/pkg/environment"
+)
+
+var (
+	getContextLong = (`Display a specific context, the current-context or all defined contexts if no name is provided`)
+
+	getContextExample = fmt.Sprintf(`# List all the contexts  airshipctl knows about
+airshipctl config get-context
+
+# Display the current context
+airshipctl config get-context --%v
+
+# Display a specific Context
+airshipctl config get-context e2e`,
+		config.FlagCurrentContext)
+)
+
+// A Context refers to a particular cluster, however it does not specify which of the cluster types
+// it relates to. Getting explicit  information about a particular context will depend
+// on the ClusterType flag.
+
+// NewCmdConfigGetContext returns a Command instance for 'config -Context' sub command
+func NewCmdConfigGetContext(rootSettings *environment.AirshipCTLSettings) *cobra.Command {
+
+	theContext := &config.ContextOptions{}
+	getcontextcmd := &cobra.Command{
+		Use:     "get-context NAME",
+		Short:   getContextLong,
+		Example: getContextExample,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			if len(args) == 1 {
+				theContext.Name = args[0]
+			}
+			return runGetContext(theContext, cmd.OutOrStdout(), rootSettings.Config())
+		},
+	}
+
+	gctxInitFlags(theContext, getcontextcmd)
+
+	return getcontextcmd
+}
+
+func gctxInitFlags(o *config.ContextOptions, getcontextcmd *cobra.Command) {
+	getcontextcmd.Flags().BoolVar(&o.CurrentContext, config.FlagCurrentContext, false,
+		config.FlagCurrentContext+" to retrieve the current context entry in airshipctl config")
+
+}
+
+// runGetContext performs the execution of 'config get-Context' sub command
+func runGetContext(o *config.ContextOptions, out io.Writer, airconfig *config.Config) error {
+	if o.Name == "" && !o.CurrentContext {
+		return getContexts(out, airconfig)
+	}
+	return getContext(o, out, airconfig)
+}
+
+func getContext(o *config.ContextOptions, out io.Writer, airconfig *config.Config) error {
+	cName := o.Name
+	if o.CurrentContext {
+		cName = airconfig.CurrentContext
+	}
+	context, err := airconfig.GetContext(cName)
+	if err != nil {
+		return err
+	}
+	fmt.Fprintf(out, "%s", context.PrettyString())
+	return nil
+}
+
+func getContexts(out io.Writer, airconfig *config.Config) error {
+	contexts, err := airconfig.GetContexts()
+	if err != nil {
+		return err
+	}
+	if contexts == nil {
+		fmt.Fprint(out, "No Contexts found in the configuration.\n")
+	}
+	for _, context := range contexts {
+		fmt.Fprintf(out, "%s", context.PrettyString())
+	}
+	return nil
+}
diff --git a/cmd/config/get_context_test.go b/cmd/config/get_context_test.go
new file mode 100644
index 000000000..9f85ec801
--- /dev/null
+++ b/cmd/config/get_context_test.go
@@ -0,0 +1,116 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+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.
+*/
+
+package config_test
+
+import (
+	"fmt"
+	"testing"
+
+	kubeconfig "k8s.io/client-go/tools/clientcmd/api"
+
+	cmd "opendev.org/airship/airshipctl/cmd/config"
+	"opendev.org/airship/airshipctl/pkg/config"
+	"opendev.org/airship/airshipctl/pkg/environment"
+	"opendev.org/airship/airshipctl/testutil"
+)
+
+const (
+	currentContextFlag = "--" + config.FlagCurrentContext
+
+	fooContext     = "ContextFoo"
+	barContext     = "ContextBar"
+	bazContext     = "ContextBaz"
+	missingContext = "contextMissing"
+)
+
+func TestGetContextCmd(t *testing.T) {
+	conf := &config.Config{
+		Contexts: map[string]*config.Context{
+			fooContext: getNamedTestContext(fooContext),
+			barContext: getNamedTestContext(barContext),
+			bazContext: getNamedTestContext(bazContext),
+		},
+		CurrentContext: bazContext,
+	}
+
+	settings := &environment.AirshipCTLSettings{}
+	settings.SetConfig(conf)
+
+	cmdTests := []*testutil.CmdTest{
+		{
+			Name:    "get-context",
+			CmdLine: fmt.Sprintf("%s", fooContext),
+			Cmd:     cmd.NewCmdConfigGetContext(settings),
+		},
+		{
+			Name:    "get-all-contexts",
+			CmdLine: fmt.Sprintf("%s %s", fooContext, barContext),
+			Cmd:     cmd.NewCmdConfigGetContext(settings),
+		},
+		// This is not implemented yet
+		{
+			Name:    "get-multiple-contexts",
+			CmdLine: fmt.Sprintf("%s %s", fooContext, barContext),
+			Cmd:     cmd.NewCmdConfigGetContext(settings),
+		},
+
+		{
+			Name:    "missing",
+			CmdLine: fmt.Sprintf("%s", missingContext),
+			Cmd:     cmd.NewCmdConfigGetContext(settings),
+			Error: fmt.Errorf("Context %s information was not "+
+				"found in the configuration.", missingContext),
+		},
+		{
+			Name:    "get-current-context",
+			CmdLine: fmt.Sprintf("%s", currentContextFlag),
+			Cmd:     cmd.NewCmdConfigGetContext(settings),
+		},
+	}
+
+	for _, tt := range cmdTests {
+		testutil.RunTest(t, tt)
+	}
+}
+
+func TestNoContextsGetContextCmd(t *testing.T) {
+	settings := &environment.AirshipCTLSettings{}
+	settings.SetConfig(&config.Config{})
+	cmdTest := &testutil.CmdTest{
+		Name:    "no-contexts",
+		CmdLine: "",
+		Cmd:     cmd.NewCmdConfigGetContext(settings),
+	}
+	testutil.RunTest(t, cmdTest)
+}
+
+func getNamedTestContext(contextName string) *config.Context {
+
+	kContext := &kubeconfig.Context{
+		Namespace: "dummy_namespace",
+		AuthInfo:  "dummy_user",
+		Cluster:   fmt.Sprintf("dummycluster_%s", config.Ephemeral),
+	}
+
+	newContext := &config.Context{
+		NameInKubeconf: fmt.Sprintf("%s_%s", contextName, config.Ephemeral),
+		Manifest:       fmt.Sprintf("Manifest_%s", contextName),
+	}
+	newContext.SetKubeContext(kContext)
+
+	return newContext
+}
diff --git a/cmd/config/set_context.go b/cmd/config/set_context.go
new file mode 100644
index 000000000..abaa157dc
--- /dev/null
+++ b/cmd/config/set_context.go
@@ -0,0 +1,141 @@
+/*
+Copyright 2016 The Kubernetes Authors.
+
+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.
+*/
+
+package config
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/spf13/cobra"
+
+	"opendev.org/airship/airshipctl/pkg/config"
+	"opendev.org/airship/airshipctl/pkg/environment"
+	conferrors "opendev.org/airship/airshipctl/pkg/errors"
+)
+
+var (
+	setContextLong = (`
+Sets a context entry in arshipctl config.
+Specifying a name that already exists will merge new fields on top of existing values for those fields.`)
+
+	setContextExample = fmt.Sprintf(`
+# Create a completely new e2e context entry
+airshipctl config set-context e2e --%v=kube-system --%v=manifest --%v=auth-info --%v=%v
+
+# Update the current-context to e2e
+airshipctl config set-context e2e --%v=true`,
+		config.FlagNamespace,
+		config.FlagManifest,
+		config.FlagAuthInfoName,
+		config.FlagClusterType,
+		config.Target,
+		config.FlagCurrentContext)
+)
+
+// NewCmdConfigSetContext creates a command object for the "set-context" action, which
+// defines a new Context airship config.
+func NewCmdConfigSetContext(rootSettings *environment.AirshipCTLSettings) *cobra.Command {
+	theContext := &config.ContextOptions{}
+
+	setcontextcmd := &cobra.Command{
+		Use:     "set-context NAME",
+		Short:   "Sets a context entry or updates current-context in the airshipctl config",
+		Long:    setContextLong,
+		Example: setContextExample,
+		Args:    cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			theContext.Name = cmd.Flags().Args()[0]
+			modified, err := runSetContext(theContext, rootSettings.Config())
+			if err != nil {
+				return err
+			}
+			if modified {
+				fmt.Fprintf(cmd.OutOrStdout(), "Context %q modified.\n", theContext.Name)
+			} else {
+				fmt.Fprintf(cmd.OutOrStdout(), "Context %q created.\n", theContext.Name)
+			}
+			return nil
+		},
+	}
+
+	sctxInitFlags(theContext, setcontextcmd)
+	return setcontextcmd
+}
+
+func sctxInitFlags(o *config.ContextOptions, setcontextcmd *cobra.Command) {
+
+	setcontextcmd.Flags().BoolVar(&o.CurrentContext, config.FlagCurrentContext, false,
+		config.FlagCurrentContext+" for the context entry in airshipctl config")
+
+	setcontextcmd.Flags().StringVar(&o.Cluster, config.FlagClusterName, o.Cluster,
+		config.FlagClusterName+" for the context entry in airshipctl config")
+
+	setcontextcmd.Flags().StringVar(&o.AuthInfo, config.FlagAuthInfoName, o.AuthInfo,
+		config.FlagAuthInfoName+" for the context entry in airshipctl config")
+
+	setcontextcmd.Flags().StringVar(&o.Manifest, config.FlagManifest, o.Manifest,
+		config.FlagManifest+" for the context entry in airshipctl config")
+
+	setcontextcmd.Flags().StringVar(&o.Namespace, config.FlagNamespace, o.Namespace,
+		config.FlagNamespace+" for the context entry in airshipctl config")
+
+	setcontextcmd.Flags().StringVar(&o.ClusterType, config.FlagClusterType, "",
+		config.FlagClusterType+" for the context entry in airshipctl config")
+
+}
+
+func runSetContext(o *config.ContextOptions, airconfig *config.Config) (bool, error) {
+	contextWasModified := false
+	err := o.Validate()
+	if err != nil {
+		return contextWasModified, err
+	}
+
+	contextIWant := o.Name
+	context, err := airconfig.GetContext(contextIWant)
+	if err != nil {
+		var cerr conferrors.ErrMissingConfig
+		if !errors.As(err, &cerr) {
+			// An error occurred, but it wasn't a "missing" config error.
+			return contextWasModified, err
+		}
+
+		if o.CurrentContext {
+			return contextWasModified, conferrors.ErrMissingConfig{}
+		}
+		// context didn't exist, create it
+		// ignoring the returned added context
+		airconfig.AddContext(o)
+	} else {
+		// Found the desired Current Context
+		// Lets update it and be done.
+		if o.CurrentContext {
+			airconfig.CurrentContext = o.Name
+		} else {
+			// Context exists, lets update
+			airconfig.ModifyContext(context, o)
+		}
+		contextWasModified = true
+	}
+	// Update configuration file just in time persistence approach
+	if err := airconfig.PersistConfig(); err != nil {
+		// Error that it didnt persist the changes
+		return contextWasModified, conferrors.ErrConfigFailed{}
+	}
+
+	return contextWasModified, nil
+}
diff --git a/cmd/config/set_context_test.go b/cmd/config/set_context_test.go
new file mode 100644
index 000000000..38bc13a8b
--- /dev/null
+++ b/cmd/config/set_context_test.go
@@ -0,0 +1,190 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+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.
+*/
+
+package config
+
+import (
+	"bytes"
+	"testing"
+
+	kubeconfig "k8s.io/client-go/tools/clientcmd/api"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	"opendev.org/airship/airshipctl/pkg/config"
+	"opendev.org/airship/airshipctl/pkg/environment"
+	"opendev.org/airship/airshipctl/testutil"
+)
+
+const (
+	testUser = "admin@kubernetes"
+)
+
+type setContextTest struct {
+	description    string
+	config         *config.Config
+	args           []string
+	flags          []string
+	expected       string
+	expectedConfig *config.Config
+}
+
+func TestConfigSetContext(t *testing.T) {
+
+	cmdTests := []*testutil.CmdTest{
+		{
+			Name:    "config-cmd-set-context-with-help",
+			CmdLine: "--help",
+			Cmd:     NewCmdConfigSetContext(nil),
+		},
+	}
+
+	for _, tt := range cmdTests {
+		testutil.RunTest(t, tt)
+	}
+}
+
+func TestSetContext(t *testing.T) {
+
+	conf := config.InitConfig(t)
+
+	tname := "dummycontext"
+	tctype := config.Ephemeral
+
+	expconf := config.InitConfig(t)
+	expconf.Contexts[tname] = config.NewContext()
+	clusterName := config.NewClusterComplexName()
+	clusterName.WithType(tname, tctype)
+	expconf.Contexts[tname].NameInKubeconf = clusterName.Name()
+	expconf.Contexts[tname].Manifest = "edge_cloud"
+
+	expkContext := kubeconfig.NewContext()
+	expkContext.AuthInfo = testUser
+	expkContext.Namespace = "kube-system"
+	expconf.KubeConfig().Contexts[expconf.Contexts[tname].NameInKubeconf] = expkContext
+
+	test := setContextTest{
+		description: "Testing 'airshipctl config set-context' with a new context",
+		config:      conf,
+		args:        []string{tname},
+		flags: []string{
+			"--" + config.FlagClusterType + "=" + config.Target,
+			"--" + config.FlagAuthInfoName + "=" + testUser,
+			"--" + config.FlagManifest + "=edge_cloud",
+			"--" + config.FlagNamespace + "=kube-system",
+		},
+		expected:       `Context "` + tname + `" created.` + "\n",
+		expectedConfig: expconf,
+	}
+	test.run(t)
+}
+
+func TestSetCurrentContext(t *testing.T) {
+	tname := "def_target"
+	conf := config.InitConfig(t)
+
+	expconf := config.InitConfig(t)
+	expconf.CurrentContext = "def_target"
+
+	test := setContextTest{
+		description: "Testing 'airshipctl config set-context' with a new current context",
+		config:      conf,
+		args:        []string{tname},
+		flags: []string{
+			"--" + config.FlagCurrentContext + "=true",
+		},
+		expected:       `Context "` + tname + `" modified.` + "\n",
+		expectedConfig: expconf,
+	}
+	test.run(t)
+}
+func TestModifyContext(t *testing.T) {
+	tname := testCluster
+	tctype := config.Ephemeral
+
+	conf := config.InitConfig(t)
+	conf.Contexts[tname] = config.NewContext()
+
+	clusterName := config.NewClusterComplexName()
+	clusterName.WithType(tname, tctype)
+	conf.Contexts[tname].NameInKubeconf = clusterName.Name()
+	kContext := kubeconfig.NewContext()
+	kContext.AuthInfo = testUser
+	conf.KubeConfig().Contexts[clusterName.Name()] = kContext
+	conf.Contexts[tname].SetKubeContext(kContext)
+
+	expconf := config.InitConfig(t)
+	expconf.Contexts[tname] = config.NewContext()
+	expconf.Contexts[tname].NameInKubeconf = clusterName.Name()
+	expkContext := kubeconfig.NewContext()
+	expkContext.AuthInfo = testUser
+	expconf.KubeConfig().Contexts[clusterName.Name()] = expkContext
+	expconf.Contexts[tname].SetKubeContext(expkContext)
+
+	test := setContextTest{
+		description: "Testing 'airshipctl config set-context' with an existing context",
+		config:      conf,
+		args:        []string{tname},
+		flags: []string{
+			"--" + config.FlagAuthInfoName + "=" + testUser,
+		},
+		expected:       `Context "` + tname + `" modified.` + "\n",
+		expectedConfig: expconf,
+	}
+	test.run(t)
+}
+
+func (test setContextTest) run(t *testing.T) {
+
+	// Get the Environment
+	settings := &environment.AirshipCTLSettings{}
+	settings.SetConfig(test.config)
+
+	buf := bytes.NewBuffer([]byte{})
+
+	cmd := NewCmdConfigSetContext(settings)
+	cmd.SetOutput(buf)
+	cmd.SetArgs(test.args)
+	err := cmd.Flags().Parse(test.flags)
+	require.NoErrorf(t, err, "unexpected error flags args to command: %v,  flags: %v", err, test.flags)
+
+	// Execute the Command
+	// Which should Persist the File
+	err = cmd.Execute()
+	require.NoErrorf(t, err, "unexpected error executing command: %v, args: %v, flags: %v", err, test.args, test.flags)
+
+	afterRunConf := settings.Config()
+
+	// Find the Context Created or Modified
+	afterRunContext, err := afterRunConf.GetContext(test.args[0])
+	require.NoError(t, err)
+	require.NotNil(t, afterRunContext)
+
+	afterKcontext := afterRunContext.KubeContext()
+	require.NotNil(t, afterKcontext)
+
+	testKcontext := test.expectedConfig.KubeConfig().Contexts[test.expectedConfig.Contexts[test.args[0]].NameInKubeconf]
+	require.NotNil(t, testKcontext)
+
+	assert.EqualValues(t, afterKcontext.AuthInfo, testKcontext.AuthInfo)
+
+	// Test that the Return Message looks correct
+	if len(test.expected) != 0 {
+		assert.EqualValuesf(t, buf.String(), test.expected, "expected %v, but got %v", test.expected, buf.String())
+	}
+
+}
diff --git a/cmd/config/testdata/TestConfigGoldenOutput/config-cmd-with-defaults.golden b/cmd/config/testdata/TestConfigGoldenOutput/config-cmd-with-defaults.golden
index 79a8d07b7..0b45f824e 100644
--- a/cmd/config/testdata/TestConfigGoldenOutput/config-cmd-with-defaults.golden
+++ b/cmd/config/testdata/TestConfigGoldenOutput/config-cmd-with-defaults.golden
@@ -5,9 +5,11 @@ Usage:
   config [command]
 
 Available Commands:
-  get-cluster Display a specific cluster
+  get-cluster Display a specific cluster or all defined clusters if no name is provided
+  get-context Display a specific context, the current-context or all defined contexts if no name is provided
   help        Help about any command
   set-cluster Sets a cluster entry in the airshipctl config
+  set-context Sets a context entry or updates current-context in the airshipctl config
 
 Flags:
   -h, --help   help for config
diff --git a/cmd/config/testdata/TestConfigGoldenOutput/config-cmd-with-help.golden b/cmd/config/testdata/TestConfigGoldenOutput/config-cmd-with-help.golden
index 79a8d07b7..0b45f824e 100644
--- a/cmd/config/testdata/TestConfigGoldenOutput/config-cmd-with-help.golden
+++ b/cmd/config/testdata/TestConfigGoldenOutput/config-cmd-with-help.golden
@@ -5,9 +5,11 @@ Usage:
   config [command]
 
 Available Commands:
-  get-cluster Display a specific cluster
+  get-cluster Display a specific cluster or all defined clusters if no name is provided
+  get-context Display a specific context, the current-context or all defined contexts if no name is provided
   help        Help about any command
   set-cluster Sets a cluster entry in the airshipctl config
+  set-context Sets a context entry or updates current-context in the airshipctl config
 
 Flags:
   -h, --help   help for config
diff --git a/cmd/config/testdata/TestConfigSetContextGoldenOutput/config-cmd-set-context-with-help.golden b/cmd/config/testdata/TestConfigSetContextGoldenOutput/config-cmd-set-context-with-help.golden
new file mode 100644
index 000000000..9d373be24
--- /dev/null
+++ b/cmd/config/testdata/TestConfigSetContextGoldenOutput/config-cmd-set-context-with-help.golden
@@ -0,0 +1,23 @@
+
+Sets a context entry in arshipctl config.
+Specifying a name that already exists will merge new fields on top of existing values for those fields.
+
+Usage:
+  set-context NAME [flags]
+
+Examples:
+
+# Create a completely new e2e context entry
+airshipctl config set-context e2e --namespace=kube-system --manifest=manifest --user=auth-info --cluster-type=target
+
+# Update the current-context to e2e
+airshipctl config set-context e2e --current-context=true
+
+Flags:
+      --cluster string        cluster for the context entry in airshipctl config
+      --cluster-type string   cluster-type for the context entry in airshipctl config
+      --current-context       current-context for the context entry in airshipctl config
+  -h, --help                  help for set-context
+      --manifest string       manifest for the context entry in airshipctl config
+      --namespace string      namespace for the context entry in airshipctl config
+      --user string           user for the context entry in airshipctl config
diff --git a/cmd/config/testdata/TestGetContextCmdGoldenOutput/get-all-contexts.golden b/cmd/config/testdata/TestGetContextCmdGoldenOutput/get-all-contexts.golden
new file mode 100644
index 000000000..946c34334
--- /dev/null
+++ b/cmd/config/testdata/TestGetContextCmdGoldenOutput/get-all-contexts.golden
@@ -0,0 +1,27 @@
+Context: ContextBar
+context-kubeconf: ContextBar_ephemeral
+manifest: Manifest_ContextBar
+
+LocationOfOrigin: ""
+cluster: dummycluster_ephemeral
+namespace: dummy_namespace
+user: dummy_user
+
+Context: ContextBaz
+context-kubeconf: ContextBaz_ephemeral
+manifest: Manifest_ContextBaz
+
+LocationOfOrigin: ""
+cluster: dummycluster_ephemeral
+namespace: dummy_namespace
+user: dummy_user
+
+Context: ContextFoo
+context-kubeconf: ContextFoo_ephemeral
+manifest: Manifest_ContextFoo
+
+LocationOfOrigin: ""
+cluster: dummycluster_ephemeral
+namespace: dummy_namespace
+user: dummy_user
+
diff --git a/cmd/config/testdata/TestGetContextCmdGoldenOutput/get-context.golden b/cmd/config/testdata/TestGetContextCmdGoldenOutput/get-context.golden
new file mode 100644
index 000000000..3aecd1940
--- /dev/null
+++ b/cmd/config/testdata/TestGetContextCmdGoldenOutput/get-context.golden
@@ -0,0 +1,9 @@
+Context: ContextFoo
+context-kubeconf: ContextFoo_ephemeral
+manifest: Manifest_ContextFoo
+
+LocationOfOrigin: ""
+cluster: dummycluster_ephemeral
+namespace: dummy_namespace
+user: dummy_user
+
diff --git a/cmd/config/testdata/TestGetContextCmdGoldenOutput/get-current-context.golden b/cmd/config/testdata/TestGetContextCmdGoldenOutput/get-current-context.golden
new file mode 100644
index 000000000..bbcedb6bd
--- /dev/null
+++ b/cmd/config/testdata/TestGetContextCmdGoldenOutput/get-current-context.golden
@@ -0,0 +1,9 @@
+Context: ContextBaz
+context-kubeconf: ContextBaz_ephemeral
+manifest: Manifest_ContextBaz
+
+LocationOfOrigin: ""
+cluster: dummycluster_ephemeral
+namespace: dummy_namespace
+user: dummy_user
+
diff --git a/cmd/config/testdata/TestGetContextCmdGoldenOutput/get-multiple-contexts.golden b/cmd/config/testdata/TestGetContextCmdGoldenOutput/get-multiple-contexts.golden
new file mode 100644
index 000000000..946c34334
--- /dev/null
+++ b/cmd/config/testdata/TestGetContextCmdGoldenOutput/get-multiple-contexts.golden
@@ -0,0 +1,27 @@
+Context: ContextBar
+context-kubeconf: ContextBar_ephemeral
+manifest: Manifest_ContextBar
+
+LocationOfOrigin: ""
+cluster: dummycluster_ephemeral
+namespace: dummy_namespace
+user: dummy_user
+
+Context: ContextBaz
+context-kubeconf: ContextBaz_ephemeral
+manifest: Manifest_ContextBaz
+
+LocationOfOrigin: ""
+cluster: dummycluster_ephemeral
+namespace: dummy_namespace
+user: dummy_user
+
+Context: ContextFoo
+context-kubeconf: ContextFoo_ephemeral
+manifest: Manifest_ContextFoo
+
+LocationOfOrigin: ""
+cluster: dummycluster_ephemeral
+namespace: dummy_namespace
+user: dummy_user
+
diff --git a/cmd/config/testdata/TestGetContextCmdGoldenOutput/missing.golden b/cmd/config/testdata/TestGetContextCmdGoldenOutput/missing.golden
new file mode 100644
index 000000000..8862d63a0
--- /dev/null
+++ b/cmd/config/testdata/TestGetContextCmdGoldenOutput/missing.golden
@@ -0,0 +1,18 @@
+Error: Missing configuration
+Usage:
+  get-context NAME [flags]
+
+Examples:
+# List all the contexts  airshipctl knows about
+airshipctl config get-context
+
+# Display the current context
+airshipctl config get-context --current-context
+
+# Display a specific Context
+airshipctl config get-context e2e
+
+Flags:
+      --current-context   current-context to retrieve the current context entry in airshipctl config
+  -h, --help              help for get-context
+
diff --git a/cmd/config/testdata/TestNoContextsGetContextCmdGoldenOutput/no-contexts.golden b/cmd/config/testdata/TestNoContextsGetContextCmdGoldenOutput/no-contexts.golden
new file mode 100644
index 000000000..e69de29bb
diff --git a/pkg/config/cmds.go b/pkg/config/cmds.go
index 1b4901eac..5cf769393 100644
--- a/pkg/config/cmds.go
+++ b/pkg/config/cmds.go
@@ -47,3 +47,18 @@ func (o *ClusterOptions) Validate() error {
 	}
 	return nil
 }
+
+func (o *ContextOptions) Validate() error {
+	if len(o.Name) == 0 {
+		return errors.New("you must specify a non-empty context name")
+	}
+	// Expect ClusterType only when this is not setting currentContext
+	if o.ClusterType != "" {
+		err := ValidClusterType(o.ClusterType)
+		if err != nil {
+			return err
+		}
+	}
+	// TODO Manifest, Cluster could be validated against the existing config maps
+	return nil
+}
diff --git a/pkg/config/cmds_test.go b/pkg/config/cmds_test.go
index 4d0ec91e8..cae23e8b8 100644
--- a/pkg/config/cmds_test.go
+++ b/pkg/config/cmds_test.go
@@ -22,7 +22,7 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestValidate(t *testing.T) {
+func TestValidateCluster(t *testing.T) {
 	co := DummyClusterOptions()
 
 	// Assert that the initial dummy config is valid
@@ -61,3 +61,11 @@ func TestValidate(t *testing.T) {
 	err = co.Validate()
 	assert.Error(t, err)
 }
+
+func TestValidateContext(t *testing.T) {
+	co := DummyContextOptions()
+	// Valid Data case
+	err := co.Validate()
+	assert.NoError(t, err)
+
+}
diff --git a/pkg/config/cmds_types.go b/pkg/config/cmds_types.go
index d862d4f5a..a6190ebc9 100644
--- a/pkg/config/cmds_types.go
+++ b/pkg/config/cmds_types.go
@@ -8,3 +8,13 @@ type ClusterOptions struct {
 	CertificateAuthority  string
 	EmbedCAData           bool
 }
+
+type ContextOptions struct {
+	Name           string
+	ClusterType    string
+	CurrentContext bool
+	Cluster        string
+	AuthInfo       string
+	Manifest       string
+	Namespace      string
+}
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 5da620893..74288ec82 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -32,6 +32,7 @@ import (
 
 	kubeconfig "k8s.io/client-go/tools/clientcmd/api"
 
+	conferrors "opendev.org/airship/airshipctl/pkg/errors"
 	"opendev.org/airship/airshipctl/pkg/util"
 )
 
@@ -97,7 +98,7 @@ func (c *Config) reconcileConfig() error {
 
 	// I changed things during the reconciliation
 	// Lets reflect them in the config files
-	// Specially useful if the cnofig is loaded during a get operation
+	// Specially useful if the config is loaded during a get operation
 	// If it was a Set this would have happened eventually any way
 	if persistIt {
 		return c.PersistConfig()
@@ -211,6 +212,7 @@ func (c *Config) reconcileContexts(updatedClusterNames map[string]string) {
 		}
 		// Make sure the name matches
 		c.Contexts[key].NameInKubeconf = context.Cluster
+		c.Contexts[key].SetKubeContext(context)
 
 		// What about if a Context refers to a properly named cluster
 		// that does not exist in airship config
@@ -267,8 +269,6 @@ func (c *Config) reconcileCurrentContext() {
 			c.kubeConfig.CurrentContext = c.CurrentContext
 		}
 	}
-	c.kubeConfig.CurrentContext = ""
-	c.CurrentContext = ""
 }
 
 // This is called by users of the config to make sure that they have
@@ -370,7 +370,6 @@ func (c *Config) KubeConfig() *kubeconfig.Config {
 	return c.kubeConfig
 }
 
-// This might be changed later to be generalized
 func (c *Config) ClusterNames() []string {
 	names := []string{}
 	for k := range c.Clusters {
@@ -378,7 +377,15 @@ func (c *Config) ClusterNames() []string {
 	}
 	sort.Strings(names)
 	return names
+}
 
+func (c *Config) ContextNames() []string {
+	names := []string{}
+	for k := range c.Contexts {
+		names = append(names, k)
+	}
+	sort.Strings(names)
+	return names
 }
 
 // Get A Cluster
@@ -476,12 +483,117 @@ func (c *Config) GetClusters() ([]*Cluster, error) {
 			if err == nil {
 				clusters = append(clusters, cluster)
 			}
-
 		}
 	}
 	return clusters, nil
 }
 
+// Context Operations from Config point of view
+// Get Context
+func (c *Config) GetContext(cName string) (*Context, error) {
+	context, exists := c.Contexts[cName]
+	if !exists {
+		return nil, conferrors.ErrMissingConfig{}
+	}
+	return context, nil
+}
+
+func (c *Config) GetContexts() ([]*Context, error) {
+	contexts := []*Context{}
+	// Given that we change the testing metholdogy
+	// The ordered names are no longer required
+	for _, cName := range c.ContextNames() {
+		context, err := c.GetContext(cName)
+		if err == nil {
+			contexts = append(contexts, context)
+		}
+	}
+	return contexts, nil
+}
+
+func (c *Config) AddContext(theContext *ContextOptions) *Context {
+	// Create the new Airship config context
+	nContext := NewContext()
+	c.Contexts[theContext.Name] = nContext
+	// Create a new Kubeconfig Context object as well
+	kContext := kubeconfig.NewContext()
+	nContext.NameInKubeconf = theContext.Name
+	contextName := NewClusterComplexName()
+	contextName.WithType(theContext.Name, theContext.ClusterType)
+
+	nContext.SetKubeContext(kContext)
+	c.KubeConfig().Contexts[theContext.Name] = kContext
+
+	// Ok , I have initialized structs for the Context information
+	// We can use Modify to populate the correct information
+	c.ModifyContext(nContext, theContext)
+	return nContext
+
+}
+
+func (c *Config) ModifyContext(context *Context, theContext *ContextOptions) {
+	kContext := context.KubeContext()
+	if kContext == nil {
+		return
+	}
+	if theContext.Cluster != "" {
+		kContext.Cluster = theContext.Cluster
+	}
+	if theContext.AuthInfo != "" {
+		kContext.AuthInfo = theContext.AuthInfo
+	}
+	if theContext.Manifest != "" {
+		context.Manifest = theContext.Manifest
+	}
+	if theContext.Namespace != "" {
+		kContext.Namespace = theContext.Namespace
+	}
+}
+
+// CurrentContext methods Returns the appropriate information for the current context
+// Current Context holds labels for the approriate config objects
+//      Cluster is the name of the cluster for this context
+//      ClusterType is the name of the clustertype for this context, it should be a flag we pass to it??
+//      AuthInfo is the name of the authInfo for this context
+//      Manifest is the default manifest to be use with this context
+// Purpose for this method is simplifying the current context information
+func (c *Config) GetCurrentContext() (*Context, error) {
+	if err := c.EnsureComplete(); err != nil {
+		return nil, err
+	}
+	currentContext, err := c.GetContext(c.CurrentContext)
+	if err != nil {
+		// this should not happen since Ensure Complete checks for this
+		return nil, err
+	}
+	return currentContext, nil
+}
+func (c *Config) CurrentContextCluster() (*Cluster, error) {
+	currentContext, err := c.GetCurrentContext()
+	if err != nil {
+		return nil, err
+	}
+
+	return c.Clusters[currentContext.KubeContext().Cluster].ClusterTypes[currentContext.ClusterType()], nil
+}
+
+func (c *Config) CurrentContextAuthInfo() (*AuthInfo, error) {
+	currentContext, err := c.GetCurrentContext()
+	if err != nil {
+		return nil, err
+	}
+
+	return c.AuthInfos[currentContext.KubeContext().AuthInfo], nil
+}
+func (c *Config) CurrentContextManifest() (*Manifest, error) {
+	currentContext, err := c.GetCurrentContext()
+	if err != nil {
+		return nil, err
+	}
+
+	return c.Manifests[currentContext.Manifest], nil
+}
+
 // Purge removes the config file
 func (c *Config) Purge() error {
 	//configFile := c.ConfigFile()
@@ -550,15 +662,44 @@ func (c *Context) Equal(d *Context) bool {
 		return d == c
 	}
 	return c.NameInKubeconf == d.NameInKubeconf &&
-		c.Manifest == d.Manifest
+		c.Manifest == d.Manifest &&
+		c.kContext == d.kContext
 }
 
 func (c *Context) String() string {
-	yaml, err := yaml.Marshal(&c)
+	cyaml, err := yaml.Marshal(&c)
 	if err != nil {
 		return ""
 	}
-	return string(yaml)
+	kcluster := c.KubeContext()
+	kyaml, err := yaml.Marshal(&kcluster)
+	if err != nil {
+		return string(cyaml)
+	}
+	return fmt.Sprintf("%s\n%s", string(cyaml), string(kyaml))
+}
+
+func (c *Context) PrettyString() string {
+	clusterName := NewClusterComplexName()
+	clusterName.FromName(c.NameInKubeconf)
+
+	return fmt.Sprintf("Context: %s\n%s\n",
+		clusterName.ClusterName(), c.String())
+}
+
+func (c *Context) KubeContext() *kubeconfig.Context {
+	return c.kContext
+}
+
+func (c *Context) SetKubeContext(kc *kubeconfig.Context) {
+	c.kContext = kc
+}
+
+func (c *Context) ClusterType() string {
+	clusterName := NewClusterComplexName()
+	clusterName.FromName(c.NameInKubeconf)
+	return clusterName.ClusterType()
+
 }
 
 // AuthInfo functions
@@ -695,3 +836,11 @@ func KClusterString(kCluster *kubeconfig.Cluster) string {
 
 	return string(yaml)
 }
+func KContextString(kContext *kubeconfig.Context) string {
+	yaml, err := yaml.Marshal(&kContext)
+	if err != nil {
+		return ""
+	}
+
+	return string(yaml)
+}
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index df76596e3..1a33bc45e 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -232,11 +232,6 @@ func TestPurge(t *testing.T) {
 	assert.Falsef(t, os.IsExist(err), "Purge failed to remove file at %v", config.LoadedConfigPath())
 }
 
-func TestClusterNames(t *testing.T) {
-	conf := InitConfig(t)
-	expected := []string{"def", "onlyinkubeconf", "wrongonlyinconfig", "wrongonlyinkubeconf"}
-	assert.EqualValues(t, expected, conf.ClusterNames())
-}
 func TestKClusterString(t *testing.T) {
 	conf := InitConfig(t)
 	kClusters := conf.KubeConfig().Clusters
@@ -245,6 +240,14 @@ func TestKClusterString(t *testing.T) {
 	}
 	assert.EqualValues(t, KClusterString(nil), "null\n")
 }
+func TestKContextString(t *testing.T) {
+	conf := InitConfig(t)
+	kContexts := conf.KubeConfig().Contexts
+	for kCtx := range kContexts {
+		assert.NotEmpty(t, KContextString(kContexts[kCtx]))
+	}
+	assert.EqualValues(t, KClusterString(nil), "null\n")
+}
 func TestComplexName(t *testing.T) {
 	cName := "aCluster"
 	ctName := Ephemeral
@@ -372,3 +375,25 @@ func TestReconcileClusters(t *testing.T) {
 	// Check that the "stragglers" were removed from the airshipconfig
 	assert.NotContains(t, testConfig.Clusters, "straggler")
 }
+
+func TestGetContexts(t *testing.T) {
+	conf := InitConfig(t)
+	contexts, err := conf.GetContexts()
+	require.NoError(t, err)
+	assert.Len(t, contexts, 3)
+}
+
+func TestGetContext(t *testing.T) {
+	conf := InitConfig(t)
+	context, err := conf.GetContext("def_ephemeral")
+	require.NoError(t, err)
+
+	// Test Positives
+	assert.EqualValues(t, context.NameInKubeconf, "def_ephemeral")
+	assert.EqualValues(t, context.KubeContext().Cluster, "def_ephemeral")
+
+	// Test Wrong Cluster
+	_, err = conf.GetContext("unknown")
+	assert.Error(t, err)
+
+}
diff --git a/pkg/config/constants.go b/pkg/config/constants.go
index 00a2baadc..d06c73e8f 100644
--- a/pkg/config/constants.go
+++ b/pkg/config/constants.go
@@ -28,23 +28,24 @@ const (
 
 // Constants defining CLI flags
 const (
+	FlagAPIServer        = "server"
+	FlagAuthInfoName     = "user"
+	FlagBearerToken      = "token"
+	FlagCAFile           = "certificate-authority"
+	FlagCertFile         = "client-certificate"
 	FlagClusterName      = "cluster"
 	FlagClusterType      = "cluster-type"
-	FlagAuthInfoName     = "user"
 	FlagContext          = "context"
+	FlagCurrentContext   = "current-context"
 	FlagConfigFilePath   = AirshipConfigEnv
-	FlagNamespace        = "namespace"
-	FlagAPIServer        = "server"
-	FlagInsecure         = "insecure-skip-tls-verify"
-	FlagCertFile         = "client-certificate"
-	FlagKeyFile          = "client-key"
-	FlagCAFile           = "certificate-authority"
 	FlagEmbedCerts       = "embed-certs"
-	FlagBearerToken      = "token"
 	FlagImpersonate      = "as"
 	FlagImpersonateGroup = "as-group"
-	FlagUsername         = "username"
+	FlagInsecure         = "insecure-skip-tls-verify"
+	FlagKeyFile          = "client-key"
+	FlagManifest         = "manifest"
+	FlagNamespace        = "namespace"
 	FlagPassword         = "password"
 	FlagTimeout          = "request-timeout"
-	FlagManifest         = "manifest"
+	FlagUsername         = "username"
 )
diff --git a/pkg/config/test_utils.go b/pkg/config/test_utils.go
index aa2eaa9f0..86773c1d7 100644
--- a/pkg/config/test_utils.go
+++ b/pkg/config/test_utils.go
@@ -60,6 +60,12 @@ func DummyContext() *Context {
 	c := NewContext()
 	c.NameInKubeconf = "dummy_cluster"
 	c.Manifest = "dummy_manifest"
+	context := kubeconfig.NewContext()
+	context.Namespace = "dummy_namespace"
+	context.AuthInfo = "dummy_user"
+	context.Cluster = "dummycluster_ephemeral"
+	c.SetKubeContext(context)
+
 	return c
 }
 
@@ -144,6 +150,17 @@ func DummyClusterOptions() *ClusterOptions {
 	return co
 }
 
+func DummyContextOptions() *ContextOptions {
+	co := &ContextOptions{}
+	co.Name = "dummy_context"
+	co.Manifest = "dummy_manifest"
+	co.AuthInfo = "dummy_user"
+	co.CurrentContext = false
+	co.Namespace = "dummy_namespace"
+
+	return co
+}
+
 const (
 	testConfigYAML = `apiVersion: airshipit.org/v1alpha1
 clusters:
diff --git a/pkg/config/testdata/context-string.yaml b/pkg/config/testdata/context-string.yaml
index c1051fe26..b4cca1a5b 100644
--- a/pkg/config/testdata/context-string.yaml
+++ b/pkg/config/testdata/context-string.yaml
@@ -1,2 +1,7 @@
 context-kubeconf: dummy_cluster
 manifest: dummy_manifest
+
+LocationOfOrigin: ""
+cluster: dummycluster_ephemeral
+namespace: dummy_namespace
+user: dummy_user
diff --git a/pkg/config/types.go b/pkg/config/types.go
index 7ab717f2e..174115d2a 100644
--- a/pkg/config/types.go
+++ b/pkg/config/types.go
@@ -97,15 +97,18 @@ type Modules struct {
 	Dummy string `json:"dummy-for-tests"`
 }
 
-// Context is a tuple of references to a cluster (how do I communicate with a kubernetes cluster),
+// Context is a tuple of references to a cluster (how do I communicate with a kubernetes context),
 // a user (how do I identify myself), and a namespace (what subset of resources do I want to work with)
 type Context struct {
-	// Context name in kubeconf. Should include a clustername following the naming conventions for airshipctl
-	// <clustername>_<clustertype>
+	// Context name in kubeconf
 	NameInKubeconf string `json:"context-kubeconf"`
+
 	// Manifest is the default manifest to be use with this context
 	// +optional
 	Manifest string `json:"manifest,omitempty"`
+
+	// Kubeconfig Context Object
+	kContext *kubeconfig.Context
 }
 
 type AuthInfo struct {
diff --git a/pkg/errors/common.go b/pkg/errors/common.go
index 16ec47ce1..80353eaab 100644
--- a/pkg/errors/common.go
+++ b/pkg/errors/common.go
@@ -1,11 +1,24 @@
 package errors
 
+// AirshipError is the base error type
+// used to create extended error types
+// in other airshipctl packages.
+type AirshipError struct {
+	Message string
+}
+
+// Error function implments the golang
+// error interface
+func (ae *AirshipError) Error() string {
+	return ae.Message
+}
+
 // ErrNotImplemented returned for not implemented features
 type ErrNotImplemented struct {
 }
 
 func (e ErrNotImplemented) Error() string {
-	return "Error. Not implemented"
+	return "Not implemented"
 }
 
 // ErrWrongConfig returned in case of incorrect configuration
@@ -13,5 +26,21 @@ type ErrWrongConfig struct {
 }
 
 func (e ErrWrongConfig) Error() string {
-	return "Error. Wrong configuration"
+	return "Wrong configuration"
+}
+
+// ErrMissingConfig returned in case of missing configuration
+type ErrMissingConfig struct {
+}
+
+func (e ErrMissingConfig) Error() string {
+	return "Missing configuration"
+}
+
+// ErrConfigFailed returned in case of failure during configuration
+type ErrConfigFailed struct {
+}
+
+func (e ErrConfigFailed) Error() string {
+	return "Configuration failed to complete."
 }