Skip to content

Commit cc06db0

Browse files
Merge pull request #14338 from jcantrill/cluster_up_logging_with_ansible
Use openshift-ansible for logging and metrics in 'oc cluster up'
2 parents d651f30 + 23dfd6c commit cc06db0

File tree

6 files changed

+415
-0
lines changed

6 files changed

+415
-0
lines changed
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
package openshift
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"io/ioutil"
7+
"path"
8+
"text/template"
9+
10+
"github.com/golang/glog"
11+
12+
kapierrors "k8s.io/apimachinery/pkg/api/errors"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
kapi "k8s.io/kubernetes/pkg/api"
15+
kbatch "k8s.io/kubernetes/pkg/apis/batch"
16+
kclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
17+
18+
"github.com/openshift/origin/pkg/bootstrap/docker/errors"
19+
"github.com/openshift/origin/pkg/bootstrap/docker/host"
20+
)
21+
22+
const (
23+
defaultAnsibleImageUser = "root"
24+
defaultOpenshiftAnsibleImage = "ansible"
25+
deploymentTypeOrigin = "origin"
26+
deploymentTypeOCP = "openshift-enterprise"
27+
imageStreamCentos = "centos7"
28+
openshiftAnsibleServiceAccount = "openshift-ansible"
29+
)
30+
31+
const defaultMetricsInventory = `
32+
[OSEv3:children]
33+
masters
34+
nodes
35+
36+
[OSEv3:vars]
37+
#openshift_release={{.OSERelease}}
38+
39+
openshift_deployment_type={{.OSEDeploymentType}}
40+
41+
openshift_metrics_install_metrics=True
42+
openshift_metrics_image_prefix={{.MetricsImagePrefix}}
43+
openshift_metrics_image_version={{.MetricsImageVersion}}
44+
openshift_metrics_resolution={{.MetricsResolution}}
45+
46+
openshift_metrics_hawkular_hostname={{.HawkularHostName}}
47+
48+
[masters]
49+
{{.MasterIP}} ansible_connection=local
50+
51+
[nodes]
52+
{{.MasterIP}}
53+
`
54+
55+
const defaultLoggingInventory = `
56+
[OSEv3:children]
57+
masters
58+
nodes
59+
60+
[OSEv3:vars]
61+
#openshift_release={{.OSERelease}}
62+
63+
openshift_deployment_type={{.OSEDeploymentType}}
64+
65+
openshift_logging_image_prefix={{.LoggingImagePrefix}}
66+
openshift_logging_image_version={{.LoggingImageVersion}}
67+
openshift_logging_master_public_url={{.MasterPublicURL}}
68+
69+
openshift_logging_install_logging=true
70+
openshift_logging_use_ops=false
71+
openshift_logging_namespace={{.LoggingNamespace}}
72+
73+
openshift_logging_elasticseach_memory_limit=1024M
74+
openshift_logging_elasticseach_storage_type=pvc
75+
openshift_logging_elasticseach_pvc_size=100G
76+
77+
openshift_logging_kibana_hostname={{.KibanaHostName}}
78+
79+
[masters]
80+
{{.MasterIP}} ansible_connection=local
81+
82+
[nodes]
83+
{{.MasterIP}}
84+
`
85+
86+
type ansibleLoggingInventoryParams struct {
87+
Template string
88+
LoggingImagePrefix string
89+
LoggingImageVersion string
90+
LoggingNamespace string
91+
KibanaHostName string
92+
}
93+
94+
type ansibleMetricsInventoryParams struct {
95+
MetricsImagePrefix string
96+
MetricsImageVersion string
97+
MetricsResolution string
98+
HawkularHostName string
99+
}
100+
101+
type ansibleInventoryParams struct {
102+
MasterIP string
103+
MasterPublicURL string
104+
OSERelease string
105+
OSEDeploymentType string
106+
ansibleLoggingInventoryParams
107+
ansibleMetricsInventoryParams
108+
}
109+
110+
type ansibleRunner struct {
111+
*Helper
112+
KubeClient kclient.Interface
113+
ImageStreams string
114+
Prefix string
115+
Namespace string
116+
}
117+
118+
func newAnsibleRunner(h *Helper, kubeClient kclient.Interface, namespace, imageStreams, prefix string) *ansibleRunner {
119+
return &ansibleRunner{
120+
Helper: h,
121+
KubeClient: kubeClient,
122+
ImageStreams: imageStreams,
123+
Prefix: prefix,
124+
Namespace: namespace,
125+
}
126+
}
127+
func newAnsibleInventoryParams() ansibleInventoryParams {
128+
return ansibleInventoryParams{
129+
ansibleLoggingInventoryParams: ansibleLoggingInventoryParams{},
130+
ansibleMetricsInventoryParams: ansibleMetricsInventoryParams{},
131+
}
132+
}
133+
134+
// writeInventoryToHost generates the inventory file given the parameters and writes
135+
// the inventory file to a temp directory on the host
136+
// return the basename of the inventory file
137+
func (r *ansibleRunner) uploadInventoryToHost(inventoryTemplate string, params ansibleInventoryParams) (string, error) {
138+
inventory, err := generateAnsibleInventory(inventoryTemplate, params, r.ImageStreams)
139+
if err != nil {
140+
return "", err
141+
}
142+
file, err := ioutil.TempFile("", "openshift-inventory")
143+
if err != nil {
144+
return "", err
145+
}
146+
_, err = file.WriteString(inventory)
147+
if err != nil {
148+
return "", err
149+
}
150+
err = file.Close()
151+
if err != nil {
152+
return "", err
153+
}
154+
glog.V(1).Infof("Wrote inventory to local file: %s", file.Name())
155+
dest := fmt.Sprintf("%s/%s.inventory", host.DefaultConfigDir, r.Prefix)
156+
glog.V(1).Infof("Uploading file %s to host destination: %s", file.Name(), dest)
157+
err = r.Helper.hostHelper.UploadFileToContainer(file.Name(), dest)
158+
if err != nil {
159+
return "", err
160+
}
161+
return path.Base(dest), nil
162+
}
163+
164+
// generateAnsibleInventory and return the content as a string
165+
func generateAnsibleInventory(inventoryTemplate string, params ansibleInventoryParams, imageStreams string) (string, error) {
166+
167+
// set the deploymentType
168+
if imageStreams == imageStreamCentos {
169+
params.OSEDeploymentType = deploymentTypeOrigin
170+
} else {
171+
params.OSEDeploymentType = deploymentTypeOCP
172+
}
173+
t, err := template.New("").Parse(inventoryTemplate)
174+
if err != nil {
175+
return "", errors.NewError("Unable to parse ansible inventory template").WithCause(err)
176+
}
177+
178+
inventory := &bytes.Buffer{}
179+
err = t.Execute(inventory, params)
180+
if err != nil {
181+
return "", errors.NewError("Unable to substitute ansible params into the inventory template: %s", params).WithCause(err)
182+
}
183+
if glog.V(1) {
184+
glog.V(1).Infof("Generated ansible inventory:\n %s\n", inventory.String())
185+
}
186+
return inventory.String(), nil
187+
188+
}
189+
190+
func (r *ansibleRunner) createServiceAccount(namespace string) error {
191+
serviceAccount := &kapi.ServiceAccount{}
192+
serviceAccount.Name = openshiftAnsibleServiceAccount
193+
_, err := r.KubeClient.Core().ServiceAccounts(namespace).Create(serviceAccount)
194+
if err != nil && !kapierrors.IsAlreadyExists(err) {
195+
return errors.NewError(fmt.Sprintf("cannot create %s service account", serviceAccount.Name)).WithCause(err).WithDetails(r.Helper.OriginLog())
196+
}
197+
// Add privileged SCC to serviceAccount
198+
if err = AddSCCToServiceAccount(r.KubeClient, "privileged", serviceAccount.Name, namespace); err != nil {
199+
return errors.NewError("cannot add privileged security context constraint to service account").WithCause(err).WithDetails(r.Helper.OriginLog())
200+
}
201+
return nil
202+
}
203+
204+
func (r *ansibleRunner) RunPlaybook(params ansibleInventoryParams, playbook, hostConfigDir, imagePrefix, imageVersion string) error {
205+
if err := r.createServiceAccount(r.Namespace); err != nil {
206+
return err
207+
}
208+
209+
inventoryBaseName, err := r.uploadInventoryToHost(params.Template, params)
210+
if err != nil {
211+
return err
212+
}
213+
214+
image := fmt.Sprintf("%s-%s:%s", imagePrefix, defaultOpenshiftAnsibleImage, imageVersion)
215+
configBind := fmt.Sprintf("%s/master:/etc/origin/master", hostConfigDir)
216+
inventoryBind := fmt.Sprintf("%s/%s:/tmp/inventory", hostConfigDir, inventoryBaseName)
217+
if glog.V(1) {
218+
glog.V(1).Infof("Running image %s with playbook: %s", image, playbook)
219+
glog.V(1).Infof("With binding: %s", configBind)
220+
glog.V(1).Infof("With binding: %s", inventoryBind)
221+
}
222+
jobName := fmt.Sprintf("openshift-ansible-%s-job", r.Prefix)
223+
env := []kapi.EnvVar{
224+
{
225+
Name: "INVENTORY_FILE",
226+
Value: "/tmp/inventory",
227+
},
228+
{
229+
Name: "PLAYBOOK_FILE",
230+
Value: playbook,
231+
},
232+
}
233+
runAsUser := int64(0)
234+
podSpec := kapi.PodSpec{
235+
DNSPolicy: kapi.DNSClusterFirst,
236+
RestartPolicy: kapi.RestartPolicyNever,
237+
ServiceAccountName: openshiftAnsibleServiceAccount,
238+
SecurityContext: &kapi.PodSecurityContext{
239+
HostNetwork: true,
240+
},
241+
Containers: []kapi.Container{
242+
{
243+
Name: jobName,
244+
Image: image,
245+
Env: env,
246+
SecurityContext: &kapi.SecurityContext{
247+
RunAsUser: &runAsUser,
248+
},
249+
VolumeMounts: []kapi.VolumeMount{
250+
{
251+
Name: "configdir",
252+
MountPath: "/etc/origin/master",
253+
},
254+
{
255+
Name: "inventoryfile",
256+
MountPath: "/tmp/inventory",
257+
},
258+
},
259+
},
260+
},
261+
Volumes: []kapi.Volume{
262+
{
263+
Name: "configdir",
264+
VolumeSource: kapi.VolumeSource{
265+
HostPath: &kapi.HostPathVolumeSource{
266+
Path: fmt.Sprintf("%s/master", hostConfigDir),
267+
},
268+
},
269+
},
270+
{
271+
Name: "inventoryfile",
272+
VolumeSource: kapi.VolumeSource{
273+
HostPath: &kapi.HostPathVolumeSource{
274+
Path: fmt.Sprintf("%s/%s", hostConfigDir, inventoryBaseName),
275+
},
276+
},
277+
},
278+
},
279+
}
280+
281+
completions := int32(1)
282+
deadline := int64(60 * 5)
283+
284+
meta := metav1.ObjectMeta{
285+
Name: jobName,
286+
}
287+
288+
job := &kbatch.Job{
289+
ObjectMeta: meta,
290+
Spec: kbatch.JobSpec{
291+
Completions: &completions,
292+
ActiveDeadlineSeconds: &deadline,
293+
Template: kapi.PodTemplateSpec{
294+
Spec: podSpec,
295+
},
296+
},
297+
}
298+
299+
// Create the job client
300+
jobClient := r.KubeClient.Batch().Jobs(r.Namespace)
301+
302+
// Submit the job
303+
_, err = jobClient.Create(job)
304+
if err != nil && kapierrors.IsAlreadyExists(err) {
305+
return nil
306+
}
307+
return err
308+
}

pkg/bootstrap/docker/openshift/helper.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,7 @@ func (h *Helper) ServerVersion() (semver.Version, error) {
620620
if h.version != nil {
621621
return *h.version, nil
622622
}
623+
623624
versionText, _, _, err := h.runHelper.New().Image(h.image).
624625
Command("version").
625626
DiscardContainer().

pkg/bootstrap/docker/openshift/logging.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const (
2020
svcKibana = "kibana-logging"
2121
loggingDeployerAccountTemplate = "logging-deployer-account-template"
2222
loggingDeployerTemplate = "logging-deployer-template"
23+
loggingPlaybook = "playbooks/byo/openshift-cluster/openshift-logging.yml"
2324
)
2425

2526
func instantiateTemplate(client client.Interface, mapper configcmd.Mapper, templateNamespace, templateName, targetNamespace string, params map[string]string) error {
@@ -50,6 +51,42 @@ func instantiateTemplate(client client.Interface, mapper configcmd.Mapper, templ
5051
return nil
5152
}
5253

54+
// InstallLoggingViaAnsible checks whether logging is installed and installs it if not already installed
55+
func (h *Helper) InstallLoggingViaAnsible(f *clientcmd.Factory, serverIP, publicHostname, loggerHost, imagePrefix, imageVersion, hostConfigDir, imageStreams string) error {
56+
_, kubeClient, err := f.Clients()
57+
if err != nil {
58+
return errors.NewError("cannot obtain API clients").WithCause(err).WithDetails(h.OriginLog())
59+
}
60+
61+
_, err = kubeClient.Core().Namespaces().Get(loggingNamespace, metav1.GetOptions{})
62+
if err == nil {
63+
// If there's no error, the logging namespace already exists and we won't initialize it
64+
return nil
65+
}
66+
67+
// Create logging namespace
68+
out := &bytes.Buffer{}
69+
err = CreateProject(f, loggingNamespace, "", "", "oc", out)
70+
if err != nil {
71+
return errors.NewError("cannot create logging project").WithCause(err).WithDetails(out.String())
72+
}
73+
74+
params := newAnsibleInventoryParams()
75+
params.Template = defaultLoggingInventory
76+
params.MasterIP = serverIP
77+
params.MasterPublicURL = fmt.Sprintf("https://%s:8443", publicHostname)
78+
params.OSERelease = imageVersion
79+
params.LoggingImagePrefix = fmt.Sprintf("%s-", imagePrefix)
80+
params.LoggingImageVersion = imageVersion
81+
params.LoggingNamespace = loggingNamespace
82+
params.KibanaHostName = loggerHost
83+
84+
runner := newAnsibleRunner(h, kubeClient, loggingNamespace, imageStreams, "logging")
85+
86+
//run logging playbook
87+
return runner.RunPlaybook(params, loggingPlaybook, hostConfigDir, imagePrefix, imageVersion)
88+
}
89+
5390
// InstallLogging checks whether logging is installed and installs it if not already installed
5491
func (h *Helper) InstallLogging(f *clientcmd.Factory, publicHostname, loggerHost, imagePrefix, imageVersion string) error {
5592
osClient, kubeClient, err := f.Clients()

0 commit comments

Comments
 (0)