Plumb through log analytics values (#274)
* plumb through log analytics values * add option to specify a log analytics file as well * use secret for log analytics
This commit is contained in:
committed by
Robbie Zhang
parent
28daffa96f
commit
ef6ae9ecf4
3
.gitignore
vendored
3
.gitignore
vendored
@@ -26,6 +26,9 @@ bin/
|
|||||||
# Test credentials file
|
# Test credentials file
|
||||||
credentials.json
|
credentials.json
|
||||||
|
|
||||||
|
# Test loganalytics file
|
||||||
|
loganalytics.json
|
||||||
|
|
||||||
# VS Code files
|
# VS Code files
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ spec:
|
|||||||
value: /etc/virtual-kubelet/cert.pem
|
value: /etc/virtual-kubelet/cert.pem
|
||||||
- name: APISERVER_KEY_LOCATION
|
- name: APISERVER_KEY_LOCATION
|
||||||
value: /etc/virtual-kubelet/key.pem
|
value: /etc/virtual-kubelet/key.pem
|
||||||
|
{{ if .Values.loganalytics.enabled }}
|
||||||
|
- name: LOG_ANALYTICS_AUTH_LOCATION
|
||||||
|
value: /etc/virtual-kubelet/loganalytics.json
|
||||||
|
{{ end }}
|
||||||
- name: VKUBELET_POD_IP
|
- name: VKUBELET_POD_IP
|
||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
|
|||||||
@@ -7,3 +7,6 @@ data:
|
|||||||
credentials.json: {{ printf "{ \"clientId\": \"%s\", \"clientSecret\": \"%s\", \"subscriptionId\": \"%s\", \"tenantId\": \"%s\", \"activeDirectoryEndpointUrl\": \"https://login.microsoftonline.com/\", \"resourceManagerEndpointUrl\": \"https://management.azure.com/\", \"activeDirectoryGraphResourceId\": \"https://graph.windows.net/\", \"sqlManagementEndpointUrl\": \"database.windows.net\", \"galleryEndpointUrl\": \"https://gallery.azure.com/\", \"managementEndpointUrl\": \"https://management.core.windows.net/\" }" (default "MISSING" .Values.env.azureClientId) (default "MISSING" .Values.env.azureClientKey) (default "MISSING" .Values.env.azureSubscriptionId) (default "MISSING" .Values.env.azureTenantId) | b64enc | quote }}
|
credentials.json: {{ printf "{ \"clientId\": \"%s\", \"clientSecret\": \"%s\", \"subscriptionId\": \"%s\", \"tenantId\": \"%s\", \"activeDirectoryEndpointUrl\": \"https://login.microsoftonline.com/\", \"resourceManagerEndpointUrl\": \"https://management.azure.com/\", \"activeDirectoryGraphResourceId\": \"https://graph.windows.net/\", \"sqlManagementEndpointUrl\": \"database.windows.net\", \"galleryEndpointUrl\": \"https://gallery.azure.com/\", \"managementEndpointUrl\": \"https://management.core.windows.net/\" }" (default "MISSING" .Values.env.azureClientId) (default "MISSING" .Values.env.azureClientKey) (default "MISSING" .Values.env.azureSubscriptionId) (default "MISSING" .Values.env.azureTenantId) | b64enc | quote }}
|
||||||
cert.pem: {{ (default "TUlTU0lORw==" .Values.env.apiserverCert) | quote }}
|
cert.pem: {{ (default "TUlTU0lORw==" .Values.env.apiserverCert) | quote }}
|
||||||
key.pem: {{ (default "TUlTU0lORw==" .Values.env.apiserverKey) | quote }}
|
key.pem: {{ (default "TUlTU0lORw==" .Values.env.apiserverKey) | quote }}
|
||||||
|
{{ if .Values.loganalytics.enabled }}
|
||||||
|
loganalytics.json: {{ printf "{\"workspaceID\": \"%s\",\"workspaceKey\": \"%s\"}" (required "workspaceID is required for loganalytics" .Values.loganalytics.workspaceID ) (required "workspaceKey is required for loganalytics" .Values.loganalytics.workspaceKey ) }}
|
||||||
|
{{ end }}
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ env:
|
|||||||
apiserverCert:
|
apiserverCert:
|
||||||
apiserverKey:
|
apiserverKey:
|
||||||
monitoredNamespace:
|
monitoredNamespace:
|
||||||
|
loganalytics:
|
||||||
|
enabled: false
|
||||||
|
workspaceID:
|
||||||
|
workspaceKey:
|
||||||
|
|
||||||
# Install Default RBAC roles and bindings
|
# Install Default RBAC roles and bindings
|
||||||
rbac:
|
rbac:
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ type ACIProvider struct {
|
|||||||
pods string
|
pods string
|
||||||
internalIP string
|
internalIP string
|
||||||
daemonEndpointPort int32
|
daemonEndpointPort int32
|
||||||
|
diagnostics *aci.ContainerGroupDiagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthConfig is the secret returned from an ImageRegistryCredential
|
// AuthConfig is the secret returned from an ImageRegistryCredential
|
||||||
@@ -155,6 +156,25 @@ func NewACIProvider(config string, rm *manager.ResourceManager, nodeName, operat
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the log analytics file has been specified, load workspace credentials from the file
|
||||||
|
if logAnalyticsAuthFile := os.Getenv("LOG_ANALYTICS_AUTH_LOCATION"); logAnalyticsAuthFile != "" {
|
||||||
|
p.diagnostics, err = aci.NewContainerGroupDiagnosticsFromFile(logAnalyticsAuthFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have both the log analytics workspace id and key, add them to the provider
|
||||||
|
// Environment variables overwrite the values provided in the file
|
||||||
|
if logAnalyticsID := os.Getenv("LOG_ANALYTICS_ID"); logAnalyticsID != "" {
|
||||||
|
if logAnalyticsKey := os.Getenv("LOG_ANALYTICS_KEY"); logAnalyticsKey != "" {
|
||||||
|
p.diagnostics, err = aci.NewContainerGroupDiagnostics(logAnalyticsID, logAnalyticsKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if rg := os.Getenv("ACI_RESOURCE_GROUP"); rg != "" {
|
if rg := os.Getenv("ACI_RESOURCE_GROUP"); rg != "" {
|
||||||
p.resourceGroup = rg
|
p.resourceGroup = rg
|
||||||
}
|
}
|
||||||
@@ -227,6 +247,7 @@ func (p *ACIProvider) CreatePod(pod *v1.Pod) error {
|
|||||||
containerGroup.ContainerGroupProperties.Containers = containers
|
containerGroup.ContainerGroupProperties.Containers = containers
|
||||||
containerGroup.ContainerGroupProperties.Volumes = volumes
|
containerGroup.ContainerGroupProperties.Volumes = volumes
|
||||||
containerGroup.ContainerGroupProperties.ImageRegistryCredentials = creds
|
containerGroup.ContainerGroupProperties.ImageRegistryCredentials = creds
|
||||||
|
containerGroup.ContainerGroupProperties.Diagnostics = p.diagnostics
|
||||||
|
|
||||||
filterServiceAccountSecretVolume(p.operatingSystem, &containerGroup)
|
filterServiceAccountSecretVolume(p.operatingSystem, &containerGroup)
|
||||||
|
|
||||||
|
|||||||
@@ -38,3 +38,29 @@ The file looks like this, in case you want to create it yourself:
|
|||||||
"managementEndpointUrl": "https://management.core.windows.net/"
|
"managementEndpointUrl": "https://management.core.windows.net/"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Log Analytics support
|
||||||
|
|
||||||
|
Log Analytics is supported through environment variables:
|
||||||
|
- `LOG_ANALYTICS_KEY`
|
||||||
|
- `LOG_ANALYTICS_ID`
|
||||||
|
|
||||||
|
You can also specify a file with these values and specify the path to it in the `LOG_ANALYTICS_AUTH_LOCATION`:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
export LOG_ANALYTICS_AUTH_LOCATION=/secure/location/loganalytics.json
|
||||||
|
```
|
||||||
|
|
||||||
|
``` powershell
|
||||||
|
$env:LOG_ANALYTICS_AUTH_LOCATION= "/secure/location/loganalytics.json"
|
||||||
|
```
|
||||||
|
|
||||||
|
The file should look like this:
|
||||||
|
|
||||||
|
``` json
|
||||||
|
{
|
||||||
|
"workspaceID": "<YOUR_LOG_ANALYTICS_WORKSPACE_ID>",
|
||||||
|
"workspaceKey": "<YOUR_LOG_ANALYTICS_WORKSPACE_KEY>"
|
||||||
|
}
|
||||||
|
```
|
||||||
39
providers/azure/client/aci/analytics.go
Normal file
39
providers/azure/client/aci/analytics.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package aci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewContainerGroupDiagnostics(logAnalyticsID, logAnalyticsKey string) (*ContainerGroupDiagnostics, error) {
|
||||||
|
|
||||||
|
if logAnalyticsID == "" || logAnalyticsKey == "" {
|
||||||
|
return nil, errors.New("Log Analytics configuration requires both the workspace ID and Key")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ContainerGroupDiagnostics{
|
||||||
|
LogAnalytics: &LogAnalyticsWorkspace{
|
||||||
|
WorkspaceID: logAnalyticsID,
|
||||||
|
WorkspaceKey: logAnalyticsKey,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContainerGroupDiagnosticsFromFile(filepath string) (*ContainerGroupDiagnostics, error) {
|
||||||
|
|
||||||
|
analyticsdata, err := ioutil.ReadFile(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Reading Log Analytics Auth file %q failed: %v", filepath, err)
|
||||||
|
}
|
||||||
|
// Unmarshal the log analytics file.
|
||||||
|
var law LogAnalyticsWorkspace
|
||||||
|
if err := json.Unmarshal(analyticsdata, &law); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ContainerGroupDiagnostics{
|
||||||
|
LogAnalytics: &law,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
38
providers/azure/client/aci/analytics_test.go
Normal file
38
providers/azure/client/aci/analytics_test.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package aci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLogAnalyticsFileParsingSuccess(t *testing.T) {
|
||||||
|
diagnostics, err := NewContainerGroupDiagnosticsFromFile("../../../../loganalytics.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if diagnostics == nil || diagnostics.LogAnalytics == nil {
|
||||||
|
t.Fatalf("Unexpected nil diagnostics. Log Analytics file not parsed correctly")
|
||||||
|
}
|
||||||
|
|
||||||
|
if diagnostics.LogAnalytics.WorkspaceID == "" || diagnostics.LogAnalytics.WorkspaceKey == "" {
|
||||||
|
t.Fatalf("Unexpected empty analytics authentication credentials. Log Analytics file not parsed correctly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogAnalyticsFileParsingFailure(t *testing.T) {
|
||||||
|
tempFile, err := ioutil.TempFile("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = NewContainerGroupDiagnosticsFromFile(tempFile.Name())
|
||||||
|
|
||||||
|
// Cleaup
|
||||||
|
tempFile.Close()
|
||||||
|
os.Remove(tempFile.Name())
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected parsing an empty Log Analytics auth file to fail, but there were no errors")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package aci
|
package aci
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -363,21 +360,8 @@ func TestCreateContainerGroupWithReadinessProbe(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func logAnalyticsWorkspaceFromFile(filepath string) (*LogAnalyticsWorkspace, error) {
|
|
||||||
analyticsdata, err := ioutil.ReadFile(filepath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Reading LogAnalyticsWorkspace file %q failed: %v", filepath, err)
|
|
||||||
}
|
|
||||||
// Unmarshal the log analytics file.
|
|
||||||
var law LogAnalyticsWorkspace
|
|
||||||
if err := json.Unmarshal(analyticsdata, &law); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &law, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateContainerGroupWithLogAnalytics(t *testing.T) {
|
func TestCreateContainerGroupWithLogAnalytics(t *testing.T) {
|
||||||
law, err := logAnalyticsWorkspaceFromFile("../../../../loganalytics.json")
|
diagnostics, err := NewContainerGroupDiagnosticsFromFile("../../../../loganalytics.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -411,9 +395,7 @@ func TestCreateContainerGroupWithLogAnalytics(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Diagnostics: &ContainerGroupDiagnostics{
|
Diagnostics: diagnostics,
|
||||||
LogAnalytics: law,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user