Skip to content

Commit f56a07f

Browse files
authored
Merge pull request #54 from philbrookes/AG-1911
json formatting and documentation
2 parents 3e76edd + 52e2259 commit f56a07f

File tree

7 files changed

+187
-46
lines changed

7 files changed

+187
-46
lines changed

README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,7 @@ around your particular mobile runtime as well as allowing us setup the different
133133
- **ClientConfig** the client config, is a resource created by aggregating together all of the available service configs. This resource is configuration
134134
required in order to consume your mobile aware services from your mobile client. It is used by the client SDKs for the various mobile services.
135135

136-
- **ServiceConfig:** the Service Config stores information about a mobile aware service and is backed by a secret. This information is then used to populate your mobile client's config.
137-
This information could be anything but often is made up of values such as the URI of the service and perhaps some headers and configuration particular to that service.
136+
- **ServiceConfig:** Contains services' information that is used to configure the Mobile SDK. For more information see [here](./doc/service_config.md).
138137

139138
- **ClientBuild** the ClientBuild is backed by a regular BuildConfig however the CLI will help you create this BuildConfig with as little effort as possible. Allowing you to focus on
140139
just the mobile parts rather than needing to understand how to setup and manage a buildconfig and builds. For example, it will help you manage build credentials, and keys and ensure the build integrates

cmd/mobile/main.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
m "github.com/aerogear/mobile-cli/pkg/client/mobile/clientset/versioned"
3030
sc "github.com/aerogear/mobile-cli/pkg/client/servicecatalog/clientset/versioned"
3131
"github.com/aerogear/mobile-cli/pkg/cmd"
32+
restclient "k8s.io/client-go/rest"
3233
)
3334

3435
func main() {
@@ -37,14 +38,19 @@ func main() {
3738
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
3839
}
3940

40-
k8Client, mobileClient, scClient := NewClientsOrDie(*kubeconfig)
41+
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
42+
if err != nil {
43+
fmt.Fprintln(os.Stderr, err.Error())
44+
os.Exit(1)
45+
}
46+
k8Client, mobileClient, scClient := NewClientsOrDie(config)
4147
var (
4248
out = os.Stdout
4349
rootCmd = cmd.NewRootCmd()
4450
clientCmd = cmd.NewClientCmd(mobileClient, out)
4551
bindCmd = cmd.NewIntegrationCmd(scClient, k8Client)
4652
serviceConfigCmd = cmd.NewServiceConfigCommand(k8Client)
47-
clientCfgCmd = cmd.NewClientConfigCmd(k8Client, out)
53+
clientCfgCmd = cmd.NewClientConfigCmd(k8Client, config.Host, out)
4854
clientBuilds = cmd.NewClientBuildsCmd()
4955
svcCmd = cmd.NewServicesCmd(scClient, k8Client, out)
5056
)
@@ -113,13 +119,7 @@ func main() {
113119

114120
// NewClientsOrDie creates a new set of clients for Kubernetes, Service Catalog and Mobile Services
115121
// if any of these clients fails to create then the process wil die.
116-
func NewClientsOrDie(configLoc string) (kubernetes.Interface, m.Interface, sc.Interface) {
117-
config, err := clientcmd.BuildConfigFromFlags("", configLoc)
118-
if err != nil {
119-
fmt.Fprintln(os.Stderr, err.Error())
120-
os.Exit(1)
121-
}
122-
122+
func NewClientsOrDie(config *restclient.Config) (kubernetes.Interface, m.Interface, sc.Interface) {
123123
// create the K8client
124124
k8client, err := kubernetes.NewForConfig(config)
125125
if err != nil {

doc/service_config.md

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
## Service Configuration
2+
3+
### Overview
4+
the Service Config stores information about a mobile service and is backed by a secret. This information is then used to populate your mobile client's config.
5+
6+
This information could be anything but often is made up of values such as the URL of the service and perhaps some headers and configuration particular to that service.
7+
8+
### Retrieving Configurations
9+
All service configs for a particular namespace can be retrieved with the following command:
10+
```sh
11+
mobile get clientconfig example_client_id --namespace=myproject
12+
```
13+
14+
Which will produce output like the following:
15+
```sh
16+
+----------------+----------------+----------------+-------------------------------------------------------+
17+
| ID | NAME | TYPE | URL |
18+
+----------------+----------------+----------------+-------------------------------------------------------+
19+
| Client ID | example_client_id | | |
20+
| fh-sync-server | fh-sync-server | fh-sync-server | https://fh-sync-server-myproject.192.168.64.74.nip.io |
21+
| keycloak | keycloak | keycloak | https://keycloak-myproject.192.168.64.74.nip.io |
22+
| prometheus | prometheus | prometheus | https://prometheus-myproject.192.168.64.74.nip.io |
23+
+----------------+----------------+----------------+-------------------------------------------------------+
24+
```
25+
26+
For more verbose config details of each service, you can request the output in JSON format instead, with the following command:
27+
```sh
28+
mobile get clientconfig example_client_id --namespace=myproject -o json
29+
```
30+
31+
Which will produce output similar to the following (newlines and indentation have been added for readability):
32+
```
33+
{
34+
"version": "1.0",
35+
"cluster_name": "192.168.64.74:8443",
36+
"namespace": "myproject",
37+
"client_id": "example_client_id",
38+
"services": [
39+
{
40+
"id": "fh-sync-server",
41+
"name": "fh-sync-server",
42+
"type": "fh-sync-server",
43+
"url": "https://fh-sync-server-myproject.192.168.64.74.nip.io",
44+
"config": {
45+
"url": "https://fh-sync-server-myproject.192.168.64.74.nip.io"
46+
}
47+
},
48+
{
49+
"id": "keycloak",
50+
"name": "keycloak",
51+
"type": "keycloak",
52+
"url": "https://keycloak-myproject.192.168.64.74.nip.io",
53+
"config": {
54+
"auth-server-url": "https://keycloak-myproject.192.168.64.74.nip.io/auth",
55+
"clientId": "juYAlRlhTyYYmOyszFa",
56+
"realm": "myproject",
57+
"resource": "juYAlRlhTyYYmOyszFa",
58+
"ssl-required": "external",
59+
"url": "https://keycloak-myproject.192.168.64.74.nip.io/auth"
60+
}
61+
},
62+
{
63+
"id": "prometheus",
64+
"name": "prometheus",
65+
"type": "prometheus",
66+
"url": "https://prometheus-myproject.192.168.64.74.nip.io",
67+
"config": {}
68+
}
69+
]
70+
}
71+
```
72+
73+
### Understanding the JSON format
74+
Firstly, the parent object in the JSON output is described below:
75+
76+
#### version
77+
The version of the JSON structure used in this response.
78+
79+
#### cluster_name
80+
An identifier of the cluster this config was retrieved from.
81+
82+
#### namespace
83+
The namespace these configs were retrieved from.
84+
85+
#### client_id
86+
The client id of the mobile application using these configs.
87+
88+
#### services
89+
An array of configuration values for the provisioned mobile services in this namespace.
90+
91+
### The service object
92+
The service object contains specific configuration values for each mobile service provisioned to a specific namespace.
93+
94+
Each service has a common set of values:
95+
#### id
96+
A unique identifier of this specific deployment of this service.
97+
98+
#### name
99+
A human-readable identifier of this service.
100+
101+
#### type
102+
A way of categorising services, e.g. Authentication, Storage, etc...
103+
104+
#### url
105+
The URL that this service can be reach at
106+
107+
#### config
108+
The config is a loosely defined object where any extra details specific to a particular service that may be required to make proper use of this service will be stored.
109+
110+
For example, in the KeyCloak service in the snippet above, the config contains the name of the realm, clientID and other details that would be required to make use of KeyCloak from a client.

pkg/cmd/clientConfig.go

+15-15
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
package cmd
1616

1717
import (
18-
"encoding/json"
1918
"fmt"
2019
"io"
2120

@@ -29,14 +28,16 @@ import (
2928
// ClientConfigCmd executes the retrieval and display of the client config
3029
type ClientConfigCmd struct {
3130
*BaseCmd
32-
k8Client kubernetes.Interface
31+
k8Client kubernetes.Interface
32+
clusterHost string
3333
}
3434

3535
// NewClientConfigCmd creates and returns a ClientConfigCmd object
36-
func NewClientConfigCmd(k8Client kubernetes.Interface, out io.Writer) *ClientConfigCmd {
36+
func NewClientConfigCmd(k8Client kubernetes.Interface, clusterHost string, out io.Writer) *ClientConfigCmd {
3737
return &ClientConfigCmd{
38-
k8Client: k8Client,
39-
BaseCmd: &BaseCmd{Out: output.NewRenderer(out)},
38+
k8Client: k8Client,
39+
clusterHost: clusterHost,
40+
BaseCmd: &BaseCmd{Out: output.NewRenderer(out)},
4041
}
4142
}
4243

@@ -82,10 +83,13 @@ kubectl plugin mobile get clientconfig`,
8283
}
8384
ret = append(ret, svcConfig)
8485
}
86+
8587
outputJSON := ServiceConfigs{
86-
Services: ret,
87-
Namespace: ns,
88-
ClientID: clientID,
88+
Version: "1",
89+
Services: ret,
90+
Namespace: ns,
91+
ClientID: clientID,
92+
ClusterName: ccc.clusterHost,
8993
}
9094
if err := ccc.Out.Render("get"+cmd.Name(), outputType(cmd.Flags()), outputJSON); err != nil {
9195
return errors.Wrap(err, fmt.Sprintf(output.FailedToOutPutInFormat, "ServiceConfig", outputType(cmd.Flags())))
@@ -98,19 +102,15 @@ kubectl plugin mobile get clientconfig`,
98102
serviceConfigList := serviceConfigs.(ServiceConfigs)
99103
var data [][]string
100104
if serviceConfigList.ClientID != "" {
101-
data = append(data, []string{"Client ID", serviceConfigList.ClientID})
105+
data = append(data, []string{"Client ID", serviceConfigList.ClientID, "", ""})
102106
}
103107
for _, service := range serviceConfigList.Services {
104-
config, err := json.Marshal(service.Config)
105-
if err != nil {
106-
return err
107-
}
108-
data = append(data, []string{service.Name, string(config)})
108+
data = append(data, []string{service.ID, service.Name, service.Type, service.URL})
109109
}
110110
table := tablewriter.NewWriter(writer)
111111
table.SetAlignment(tablewriter.ALIGN_LEFT)
112112
table.AppendBulk(data)
113-
table.SetHeader([]string{"Name", "config"})
113+
table.SetHeader([]string{"ID", "Name", "Type", "URL"})
114114
table.Render()
115115
return nil
116116
})

pkg/cmd/clientConfig_test.go

+12-4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func TestClientConfigCmd_GetClientConfigCmd(t *testing.T) {
5151
tests := []struct {
5252
name string
5353
k8Client func() kubernetes.Interface
54+
ClusterHost string
5455
namespace string
5556
args []string
5657
cobraCmd *cobra.Command
@@ -64,6 +65,7 @@ func TestClientConfigCmd_GetClientConfigCmd(t *testing.T) {
6465
return &kFake.Clientset{}
6566
},
6667
namespace: "",
68+
ClusterHost: "test",
6769
args: []string{"client-id"},
6870
cobraCmd: getFakeCbrCmd(),
6971
ExpectError: true,
@@ -73,14 +75,19 @@ func TestClientConfigCmd_GetClientConfigCmd(t *testing.T) {
7375
{
7476
name: "get client config command with no services",
7577
k8Client: func() kubernetes.Interface {
76-
return &kFake.Clientset{}
78+
fakeclient := &kFake.Clientset{}
79+
fakeclient.AddReactor("list", "secrets", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
80+
return true, &v1.SecretList{Items: []v1.Secret{}}, nil
81+
})
82+
return fakeclient
7783
},
7884
namespace: "testing-ns",
85+
ClusterHost: "test",
7986
args: []string{"client-id"},
8087
cobraCmd: getFakeCbrCmd(),
8188
ExpectError: false,
8289
ValidateOut: func(out bytes.Buffer) error {
83-
expected := `{"services":[],"namespace":"testing-ns","client_id":"client-id"}`
90+
expected := `{"version":"1","cluster_name":"test","namespace":"testing-ns","client_id":"client-id","services":[]}`
8491
if strings.TrimSpace(out.String()) != expected {
8592
return errors.New(fmt.Sprintf("expected: '%v', got: '%v'", expected, strings.TrimSpace(out.String())))
8693
}
@@ -125,11 +132,12 @@ func TestClientConfigCmd_GetClientConfigCmd(t *testing.T) {
125132
return fakeclient
126133
},
127134
namespace: "testing-ns",
135+
ClusterHost: "test",
128136
args: []string{"client-id"},
129137
cobraCmd: getFakeCbrCmd(),
130138
ExpectError: false,
131139
ValidateOut: func(out bytes.Buffer) error {
132-
expected := `{"services":[{"config":{"headers":{},"name":"test-service","uri":""},"name":"test-service"},{"config":{"headers":{}},"name":"keycloak"}],"namespace":"testing-ns","client_id":"client-id"}`
140+
expected := `{"version":"1","cluster_name":"test","namespace":"testing-ns","client_id":"client-id","services":[{"id":"test-service","name":"test-service","type":"","url":"","config":{}},{"id":"keycloak","name":"keycloak","type":"","url":"","config":{}}]}`
133141
if strings.TrimSpace(out.String()) != expected {
134142
return errors.New(fmt.Sprintf("expected: '%v', got: '%v'", expected, strings.TrimSpace(out.String())))
135143
}
@@ -140,7 +148,7 @@ func TestClientConfigCmd_GetClientConfigCmd(t *testing.T) {
140148
for _, tc := range tests {
141149
t.Run(tc.name, func(t *testing.T) {
142150
var out bytes.Buffer
143-
ccCmd := cmd.NewClientConfigCmd(tc.k8Client(), &out)
151+
ccCmd := cmd.NewClientConfigCmd(tc.k8Client(), tc.ClusterHost, &out)
144152

145153
got := ccCmd.GetClientConfigCmd()
146154
if use := got.Use; use != tc.cobraCmd.Use {

pkg/cmd/convert.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ import (
2020
v1 "k8s.io/client-go/pkg/api/v1"
2121
)
2222

23+
func isClientConfigKey(key string) bool {
24+
return key == "url" || key == "name" || key == "type" || key == "id"
25+
}
26+
2327
func convertSecretToMobileService(s v1.Secret) *Service {
2428
params := map[string]string{}
2529
for key, value := range s.Data {
26-
if key != "uri" && key != "name" {
30+
if !isClientConfigKey(key) {
2731
params[key] = string(value)
2832
}
2933
}

0 commit comments

Comments
 (0)