",{href:"#",
8 | "aria-controls":a.sTableId,"aria-label":t[c],"data-dt-idx":p,tabindex:a.iTabIndex}).html(e)).appendTo(d),a.oApi._fnBindAction(i,{action:c},m),p++)}},i;try{i=b(h).find(d.activeElement).data("dt-idx")}catch(u){}q(b(h).empty().html('').children("ul"),m);i&&b(h).find("[data-dt-idx="+i+"]").focus()};return f});
9 |
--------------------------------------------------------------------------------
/ui/static/swf/flashExport.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pinterest/soundwave/144c6640775fb3d94ad583c54de35f305737135a/ui/static/swf/flashExport.swf
--------------------------------------------------------------------------------
/ui/templates/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Soundwave 404
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | s
14 |
15 |
16 |
17 |
18 | {% include 'navbar.html' %}
19 |
20 | oops ! I dont know this route
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ui/templates/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Soundwave 500
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | {% include 'navbar.html' %}
19 |
20 | oops ! something is broken
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ui/templates/aggregations.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | soundwave aggregations
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {% include 'navbar.html' %}
22 |
23 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
58 |
59 |
--------------------------------------------------------------------------------
/ui/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Soundwave Home
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {% include 'navbar.html' %}
27 |
28 |
29 | {% include 'query.html' %}
30 |
31 |
32 |
33 |
34 |
35 |
36 |
39 |
40 | {% for table_header in table_headers %}
41 | {{ table_header }} | {% endfor %}
42 |
43 |
44 |
45 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/ui/templates/instance.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Soundwave Instance
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {% include 'navbar.html' %}
22 |
23 |
24 |
25 |
26 |
Instance Properties {{ id }}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
43 |
44 |
45 |
56 |
57 |
--------------------------------------------------------------------------------
/ui/templates/navbar.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ui/templates/query.html:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
29 |
30 |
31 |
32 |
37 |
--------------------------------------------------------------------------------
/ui/templates/reservations.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Soundwave reservations
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {% include 'navbar.html' %}
27 |
28 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | {% for table_header in table_headers %}
49 | {{ table_header }} | {% endfor %}
50 |
51 |
52 |
53 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/worker/Dockerfile:
--------------------------------------------------------------------------------
1 | # Refer https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/
2 | # for best practices maintaining this file
3 |
4 | # Build Java8 docker. Credit is to https://github.com/cogniteev/docker-oracle-java,
5 | # which is licensed under the MIT license
6 | # Pull base image.
7 | FROM ubuntu:14.04
8 |
9 | # Install Java.
10 | RUN \
11 | apt-get update && \
12 | apt-get install -y software-properties-common && \
13 | echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | debconf-set-selections && \
14 | add-apt-repository -y ppa:webupd8team/java && \
15 | apt-get update && \
16 | apt-get install -y oracle-java8-installer && \
17 | rm -rf /var/lib/apt/lists/* && \
18 | rm -rf /var/cache/oracle-jdk8-installer
19 |
20 |
21 | # Define working directory.
22 | WORKDIR /data
23 |
24 | # Define commonly used JAVA_HOME variable
25 | ENV JAVA_HOME /usr/lib/jvm/java-8-oracle
26 |
27 |
28 | # Create and set current directory
29 | WORKDIR /opt/soundwave-worker
30 |
31 | # Add the build artifact under /opt, can be overridden by docker build
32 | ARG ARTIFACT_PATH=target/soundwave-worker-0.1-SNAPSHOT-bin.tar.gz
33 | ADD $ARTIFACT_PATH /opt/soundwave-worker/
34 |
35 | # Default command to run service, do not override it in docker run unless have a good reason
36 | # Use "docker logs ID" to view stdout and stderr
37 | CMD ["scripts/run_in_container.sh"]
38 |
--------------------------------------------------------------------------------
/worker/checkstyle-suppressions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/worker/config/log4j.properties:
--------------------------------------------------------------------------------
1 | # log4j logging configuration.
2 | # root logger.
3 | log4j.rootLogger=INFO, CONSOLE, ROLLINGFILE
4 | log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
5 | log4j.appender.CONSOLE.Threshold=INFO
6 | log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
7 | log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [%t] (%F:%L) %-5p %m%n
8 | # we will have to rely on external cron job to keep the logging space consumption under control.
9 | log4j.appender.ROLLINGFILE=org.apache.log4j.DailyRollingFileAppender
10 | log4j.appender.ROLLINGFILE.Threshold=INFO
11 | log4j.appender.ROLLINGFILE.File=/var/log/soundwave/soundwaveowrker.log
12 | log4j.appender.ROLLINGFILE.append=true
13 | log4j.appender.ROLLINGFILE.DatePattern='.'yyyy-MM-dd-HH
14 | log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
15 | log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d [%-6p] [%t] %c{1} - %m%n
16 |
17 | # access logger.
18 | log4j.logger.access=INFO, ACCESSROLLINGFILE
19 | log4j.additivity.access=false
20 | # we will have to rely on external cron job to keep the logging space consumption under control.
21 | log4j.appender.ACCESSROLLINGFILE=org.apache.log4j.DailyRollingFileAppender
22 | log4j.appender.ACCESSROLLINGFILE.File=/var/log/soundwaveworker/access.log
23 | log4j.appender.ACCESSROLLINGFILE.append=true
24 | log4j.appender.ACCESSROLLINGFILE.DatePattern='.'yyyy-MM-dd-HH
25 | log4j.appender.ACCESSROLLINGFILE.layout=org.apache.log4j.PatternLayout
26 | log4j.appender.ACCESSROLLINGFILE.layout.ConversionPattern=%m%n
27 |
28 | # failed request logger.
29 | log4j.logger.failure=INFO, FAILUREROLLINGFILE
30 | log4j.additivity.failure=false
31 | # we will have to rely on external cron job to keep the logging space consumption under control.
32 | log4j.appender.FAILUREROLLINGFILE=org.apache.log4j.DailyRollingFileAppender
33 | log4j.appender.FAILUREROLLINGFILE.File=/var/log/soundwaveworker/failure.log
34 | log4j.appender.FAILUREROLLINGFILE.append=true
35 | log4j.appender.FAILUREROLLINGFILE.DatePattern='.'yyyy-MM-dd-HH
36 | log4j.appender.FAILUREROLLINGFILE.layout=org.apache.log4j.PatternLayout
37 |
38 | #Sqs events logger
39 | log4j.logger.sqsEventLogger=INFO, SQSEVENTROLLINGFILE
40 | log4j.appender.SQSEVENTROLLINGFILE=org.apache.log4j.DailyRollingFileAppender
41 | log4j.appender.SQSEVENTROLLINGFILE.File=/var/log/cmdb/sqsevents.log
42 | log4j.appender.SQSEVENTROLLINGFILE.append=true
43 | log4j.appender.SQSEVENTROLLINGFILE.DatePattern='.'yyyy-MM-dd-HH
44 | log4j.appender.SQSEVENTROLLINGFILE.layout=org.apache.log4j.PatternLayout
45 | log4j.appender.SQSEVENTROLLINGFILE.layout.ConversionPattern=%m%n
46 |
47 | #Message Processing API logger
48 | log4j.logger.messageProcessingLog=INFO, MESSAGEPROCESSINGFILE
49 | log4j.appender.MESSAGEPROCESSINGFILE=org.apache.log4j.DailyRollingFileAppender
50 | log4j.appender.MESSAGEPROCESSINGFILE.File=/var/log/soundwaveworker/messageprocessing.log
51 | log4j.appender.MESSAGEPROCESSINGFILE.append=true
52 | log4j.appender.MESSAGEPROCESSINGFILE.DatePattern='.'yyyy-MM-dd-HH
53 | log4j.appender.MESSAGEPROCESSINGFILE.layout=org.apache.log4j.PatternLayout
54 | log4j.appender.MESSAGEPROCESSINGFILE.layout.ConversionPattern=%m%n
55 |
56 |
57 | #Scheduled Job logger
58 | log4j.logger.scheduledJobLog=INFO, SCHEDULEDJOBFILE
59 | log4j.appender.SCHEDULEDJOBFILE=org.apache.log4j.DailyRollingFileAppender
60 | log4j.appender.SCHEDULEDJOBFILE.File=/var/log/soundwaveworker/scheduledjob.log
61 | log4j.appender.SCHEDULEDJOBFILE.append=true
62 | log4j.appender.SCHEDULEDJOBFILE.DatePattern='.'yyyy-MM-dd-HH
63 | log4j.appender.SCHEDULEDJOBFILE.layout=org.apache.log4j.PatternLayout
64 | log4j.appender.SCHEDULEDJOBFILE.layout.ConversionPattern=%m%n
65 |
--------------------------------------------------------------------------------
/worker/config/soundwaveworker.properties:
--------------------------------------------------------------------------------
1 | #Common Service settings
2 | thrift_server_port=8090
3 | ostrich_port=9999
4 | service_name=cmdb
5 | cluster_name=cmdb
6 | max_connection_idle_in_minute=6
7 | max_concurrent_requests=2000
8 | server_set_hostname_prefix=cmdb
9 | server_set_enabled=true
10 | enforce_serverset_hostname_prefix=true
11 | server_set_path=/discovery/soundwave/prod
12 | slow_request_threshold_millis=1000
13 |
14 | #SQS endpoint that AWS lambda pushes Ec2 notification events
15 | update_queue=
16 |
17 | #ElasticSearch
18 | es_cluster_lb=soundwave-store
19 | es_cluster_port=9300
20 | es_instance_index = soundwave_prod
21 | es_daily_snapshot_index=soundwave_ss
22 | es_cluster_name=elasticsearch
23 |
24 | #Zookeeper
25 | zk_connection_string=soundwave-zk:2181
26 | zk_path =/soundwave/prod
27 |
28 | #JobManager Settings
29 | num_subscriber=4
30 | aws_region=us-east-1
31 | aws_call_ratelimit = 2
32 | send_service_mapping_to_aws=false
33 |
34 | #Ec2 Handler setting
35 | instance_factory = com.pinterest.soundwave.aws.BasicEsInstanceFactory
36 |
37 | aws_tag_generator = com.pinterest.BasicUploadTagsGenerator
38 |
39 | #Using IAM role or not
40 | use_instance_profile = false
41 |
--------------------------------------------------------------------------------
/worker/scripts/index_settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "settings": {
3 | "number_of_shards": 1,
4 | "number_of_replicas": 1,
5 | "analyzer": {
6 | "default": {
7 | "type": "standard"
8 | }
9 | },
10 | "tokenizer": {
11 | "default": {
12 | "type": "standard"
13 | }
14 | }
15 | },
16 | "mappings": {
17 | "_default_": {
18 | "dynamic_templates": [
19 | {
20 | "template_catch": {
21 | "match": "*",
22 | "mapping": {
23 | "index": "not_analyzed"
24 | }
25 | }
26 | }
27 | ]
28 | },
29 | "instance": {
30 | "dynamic_templates": [
31 | {
32 | "template_time": {
33 | "match": "*_time",
34 | "mapping": {
35 | "type": "date",
36 | "format": "dateOptionalTime"
37 | }
38 | }
39 | },
40 | {
41 | "template_address": {
42 | "match": "*_address",
43 | "mapping": {
44 | "type": "ip"
45 | }
46 | }
47 | }
48 | ]
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/worker/scripts/provision_index.sh:
--------------------------------------------------------------------------------
1 | curl -X PUT -d @index_settings.json $1
--------------------------------------------------------------------------------
/worker/scripts/run_in_container.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Start the process inside a docker container with java installed
3 | # Some settings can be overriden by environment variables as shown below
4 |
5 | ulimit -n 65536
6 |
7 |
8 | export SERVICENAME=soundwave-worker
9 | export JAVA_MAIN=com.pinterest.soundwave.WorkerMain
10 | LOG4J_CONFIG_FILE=${LOG4J_CONFIG_FILE:=config/log4j.properties}
11 | CONFIG_FILE=${CONFIG_FILE:=config/soundwaveworker.properties}
12 | HEAP_SIZE=${HEAP_SIZE:=512m}
13 | NEW_SIZE=${NEW_SIZE:=256m}
14 |
15 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
16 | export PARENT_DIR="$(dirname $DIR)"
17 |
18 | LOG_DIR=/var/log/soundwave-worker
19 | CP=${PARENT_DIR}:${PARENT_DIR}/*:${PARENT_DIR}/lib/*
20 |
21 | java -server -Xmx${HEAP_SIZE} -Xms${HEAP_SIZE} -XX:NewSize=${NEW_SIZE} -XX:MaxNewSize=${NEW_SIZE} \
22 | -verbosegc -Xloggc:${LOG_DIR}/gc.log \
23 | -XX:+UnlockDiagnosticVMOptions -XX:ParGCCardsPerStrideChunk=4096 \
24 | -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=100 -XX:GCLogFileSize=2M \
25 | -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintClassHistogram \
26 | -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParNewGC \
27 | -XX:OnOutOfMemoryError="kill -9 %p" \
28 | -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseCMSInitiatingOccupancyOnly \
29 | -XX:ErrorFile=${LOG_DIR}/jvm_error.log \
30 | -cp ${CP} -Dlog4j.configuration=${LOG4J_CONFIG_FILE} -Dconfig.file=${CONFIG_FILE} \
31 | -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false \
32 | -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.port=10102 \
33 | -Dfile.encoding=UTF-8 \
34 | ${JAVA_MAIN}
35 |
--------------------------------------------------------------------------------
/worker/src/main/assembly/assembly.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | bin
4 |
5 |
6 | tar.gz
7 |
8 |
9 |
10 | scripts
11 | scripts
12 |
13 | *.sh
14 |
15 |
16 |
17 | config
18 | config
19 |
20 | *.properties
21 | *.xml
22 |
23 |
24 |
25 | target
26 |
27 |
28 | *.jar
29 |
30 |
31 |
32 |
33 |
34 |
35 | lib
36 |
37 | org.slf4j:slf4j-jdk14
38 |
39 | false
40 | runtime
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/worker/src/main/java/com/pinterest/AwsServiceTagUpdater.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Pinterest, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.pinterest;
17 |
18 | import com.pinterest.soundwave.aws.Ec2InstanceStore;
19 | import com.pinterest.soundwave.bean.EsInstance;
20 | import com.pinterest.soundwave.aws.CloudInstanceStore;
21 | import com.pinterest.config.Configuration;
22 |
23 | import com.amazonaws.services.ec2.model.Tag;
24 | import com.google.common.base.Preconditions;
25 | import org.apache.commons.lang3.reflect.ConstructorUtils;
26 | import org.slf4j.Logger;
27 | import org.slf4j.LoggerFactory;
28 |
29 | import java.util.Arrays;
30 | import java.util.List;
31 |
32 | public final class AwsServiceTagUpdater {
33 |
34 | private static final Logger logger = LoggerFactory.getLogger(AwsServiceTagUpdater.class);
35 | private static AwsServiceTagUpdater s_Instance = new AwsServiceTagUpdater(new Ec2InstanceStore());
36 |
37 | private CloudInstanceStore cloudStore;
38 | private UploadTagsGenerator tagsGenerator;
39 |
40 | private AwsServiceTagUpdater(CloudInstanceStore store) {
41 | Preconditions.checkNotNull(store);
42 | this.cloudStore = store;
43 | String
44 | tagsGeneratorClass =
45 | Configuration.getProperties()
46 | .getString("aws_tag_generator", "BasicUploadTagsGenerator");
47 | try {
48 | this.tagsGenerator =
49 | (UploadTagsGenerator) ConstructorUtils
50 | .invokeConstructor(Class.forName(tagsGeneratorClass), null);
51 | } catch (Exception ex) {
52 |
53 | }
54 | }
55 |
56 | public static AwsServiceTagUpdater getInstance() {
57 | return s_Instance;
58 | }
59 |
60 | public UploadTagsGenerator getTagsGenerator() {
61 | return tagsGenerator;
62 | }
63 |
64 | public void updateTags(EsInstance inst) {
65 | List updateTags = this.getTagsGenerator().getUpdateTags(inst);
66 | try {
67 | this.cloudStore.setTagsForInstances(Arrays.asList(inst.getId()), updateTags);
68 | } catch (Exception ex) {
69 | logger.warn("Faile to update tags for instance {}", inst.getId());
70 | }
71 | }
72 |
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/worker/src/main/java/com/pinterest/BasicUploadTagsGenerator.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Pinterest, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.pinterest;
17 |
18 | import com.pinterest.soundwave.bean.EsInstance;
19 |
20 | import com.amazonaws.services.ec2.model.Tag;
21 |
22 | import java.util.ArrayList;
23 | import java.util.List;
24 |
25 | public class BasicUploadTagsGenerator extends UploadTagsGenerator {
26 |
27 | @Override
28 | public List getUpdateTags(EsInstance esInstance) {
29 | return new ArrayList<>();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/worker/src/main/java/com/pinterest/DummyOwnershipDecider.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Pinterest, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.pinterest;
17 |
18 | import com.pinterest.job.OwnershipDecider;
19 |
20 | /**
21 | * A dummy ownership decider that always return true
22 | */
23 | public class DummyOwnershipDecider implements OwnershipDecider {
24 |
25 | @Override
26 | public boolean isOwner() {
27 | return true;
28 | }
29 |
30 | @Override
31 | public String getNodeName() {
32 | return "dummy";
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/worker/src/main/java/com/pinterest/DummyRecurringJob.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Pinterest, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.pinterest;
17 |
18 | import java.util.Random;
19 | import java.util.concurrent.Callable;
20 |
21 | public class DummyRecurringJob implements Callable {
22 |
23 | private static Random random = new Random(System.currentTimeMillis());
24 | private int failRate;
25 | private int executionSeconds;
26 |
27 | public DummyRecurringJob(int failRate, int executionSeconds) {
28 | this.failRate = failRate;
29 | this.executionSeconds = executionSeconds;
30 | }
31 |
32 | @Override
33 | public Boolean call() throws Exception {
34 | Thread.sleep(executionSeconds * 1000);
35 | if (failRate >= 100 - random.nextInt(100)) {
36 | //Either throw or return false
37 | if (random.nextBoolean()) {
38 | throw new Exception("Got an exception");
39 | } else {
40 | return false;
41 | }
42 | }
43 | return true;
44 |
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/worker/src/main/java/com/pinterest/UploadTagsGenerator.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Pinterest, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.pinterest;
17 |
18 |
19 | import com.pinterest.soundwave.bean.EsInstance;
20 |
21 | import com.amazonaws.services.ec2.model.Instance;
22 | import com.amazonaws.services.ec2.model.Tag;
23 | import com.google.common.base.Preconditions;
24 |
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | public abstract class UploadTagsGenerator {
29 |
30 | /**
31 | * Return a list of tags that need to be updated to the Ec2Instance.
32 | * The tags are either not in Ec2Instance tags or having different
33 | * values
34 | * @param ec2Instance
35 | * @param esInstance
36 | * @return A list of tags
37 | */
38 | public List getUpdateTags(Instance ec2Instance, EsInstance esInstance) {
39 | Preconditions.checkNotNull(ec2Instance);
40 | Preconditions.checkNotNull(esInstance);
41 | List updateTags = new ArrayList<>();
42 |
43 | List currentEc2Tag = ec2Instance.getTags();
44 | List esUploadTags = getUpdateTags(esInstance);
45 |
46 | for (Tag tag : esUploadTags) {
47 | boolean shouldUpdate = true;
48 | for (Tag ec2Tag : currentEc2Tag) {
49 | if (ec2Tag.getKey().equals(tag.getKey()) && ec2Tag.getValue().equals(tag.getValue())) {
50 | shouldUpdate = false;
51 | break;
52 | }
53 | }
54 |
55 | if (shouldUpdate) {
56 | updateTags.add(tag);
57 | }
58 | }
59 |
60 | return updateTags;
61 |
62 | }
63 |
64 | /**
65 | * Get all tags needed to be added to Ec2Instance
66 | * @param esInstance
67 | * @return A list of tags
68 | */
69 | public abstract List getUpdateTags(EsInstance esInstance);
70 | }
71 |
--------------------------------------------------------------------------------
/worker/src/main/java/com/pinterest/soundwave/WorkerMain.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Pinterest, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.pinterest.soundwave;
17 |
18 |
19 | import com.pinterest.JobManager;
20 | import com.pinterest.soundwave.aws.Ec2InstanceStore;
21 | import com.pinterest.soundwave.pinterest.EsInstanceStore;
22 | import com.pinterest.zookeeper.ZkJobInfoStore;
23 | import org.slf4j.Logger;
24 | import org.slf4j.LoggerFactory;
25 |
26 | /**
27 | * Main method that initializes logger, service framework proxy, finagle server and ostrich
28 | */
29 | public class WorkerMain {
30 |
31 | private static final Logger LOG =
32 | LoggerFactory.getLogger(WorkerMain.class);
33 | private static final Logger FAILURE_LOG = LoggerFactory.getLogger("failure");
34 | private static final Logger SLOW_LOG = LoggerFactory.getLogger("slow");
35 |
36 | private static final String MAX_CONNECTION_IDLE_CONFIG_KEY = "max_connection_idle_in_minute";
37 | private static final String MAX_CONCURRENT_REQUESTS_KEY = "max_concurrent_requests";
38 | private static final String OSTRICH_PORT_KEY = "ostrich_port";
39 | private static final String SERVICE_NAME_KEY = "service_name";
40 | private static final String CLUSTER_NAME_KEY = "cluster_name";
41 | private static final String THRIFT_PORT_KEY = "thrift_server_port";
42 | private static final String SERVER_SET_PATH_KEY = "server_set_path";
43 | private static final String SLOW_REQUEST_THRESHOLD_MILLIS = "slow_request_threshold_millis";
44 |
45 | public static void main(String[] args) {
46 | try {
47 | if (System.getProperty("config.file") == null) {
48 | System.setProperty("config.file", "soundwave.opensource.properties");
49 | }
50 | //Start the Job Manager
51 | JobManager
52 | manager =
53 | new JobManager(new ZkJobInfoStore(), new Ec2InstanceStore(), new EsInstanceStore());
54 | manager.start();
55 |
56 | while (true) {
57 | Thread.sleep(1000);
58 | }
59 |
60 | //CHECKSTYLE_OFF: IllegalCatch
61 | } catch (Exception e) {
62 | LOG.error("Cannot start the service properly", e);
63 | System.exit(1);
64 | }
65 | //CHECKSTYLE_ON: IllegalCatch
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/worker/src/main/java/com/pinterest/soundwave/job/definitions/ElasticSearchHealthCheckJob.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Pinterest, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.pinterest.soundwave.job.definitions;
17 |
18 | import com.pinterest.StatsUtil;
19 | import com.pinterest.soundwave.pinterest.EsInstanceStore;
20 |
21 | import com.twitter.ostrich.stats.Stats;
22 | import org.elasticsearch.action.admin.cluster.node.stats.NodeStats;
23 | import org.elasticsearch.action.admin.indices.stats.IndexStats;
24 |
25 | import java.util.HashMap;
26 | import java.util.Map;
27 | import java.util.concurrent.Callable;
28 |
29 | public class ElasticSearchHealthCheckJob implements Callable {
30 |
31 | private EsInstanceStore esInstanceStore = new EsInstanceStore();
32 |
33 | @Override
34 | public Boolean call() throws Exception {
35 | logNodeStats(esInstanceStore.getNodesStats());
36 | logIndexStats(esInstanceStore.getIndexStats());
37 | return true;
38 | }
39 |
40 | private void logNodeStats(Map statsMap) {
41 | Map tags = new HashMap<>();
42 | for (NodeStats stat : statsMap.values()) {
43 | tags.put("esnode", stat.getHostname());
44 | Stats.setGauge(StatsUtil.getStatsName("eshealth", "heapUsedPercent", tags),
45 | stat.getJvm().getMem().getHeapUsedPrecent());
46 | Stats.setGauge(StatsUtil.getStatsName("eshealth", "heapMaxMB", tags),
47 | stat.getJvm().getMem().getHeapMax().getMbFrac());
48 | Stats.setGauge(StatsUtil.getStatsName("eshealth", "heapUsedMB", tags),
49 | stat.getJvm().getMem().getHeapUsed().getMbFrac());
50 | Stats.setGauge(StatsUtil.getStatsName("eshealth", "upMinutes", tags),
51 | stat.getJvm().getUptime().getMinutesFrac());
52 | Stats.setGauge(StatsUtil.getStatsName("eshealth", "docCount", tags),
53 | stat.getIndices().getDocs().getCount());
54 | }
55 | }
56 |
57 | private void logIndexStats(Map indexStatsMap) {
58 | for (IndexStats stat : indexStatsMap.values()) {
59 | String indexKey = "esindex_" + stat.getIndex();
60 | Stats.setGauge(StatsUtil.getStatsName(indexKey, "primaryDocCount"),
61 | stat.getPrimaries().getDocs().getCount());
62 | Stats.setGauge(StatsUtil.getStatsName(indexKey, "totalDocCount"),
63 | stat.getTotal().getDocs().getCount());
64 | Stats.setGauge(StatsUtil.getStatsName(indexKey, "shardsCount"),
65 | stat.getShards().length);
66 | Stats.setGauge(StatsUtil.getStatsName(indexKey, "primaryStoreSizeMB"),
67 | stat.getPrimaries().getStore().getSize().getMbFrac());
68 | Stats.setGauge(StatsUtil.getStatsName(indexKey, "totalStoreSizeMB"),
69 | stat.getTotal().getStore().getSize().getMbFrac());
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/worker/src/main/java/com/pinterest/soundwave/job/definitions/HealthCheckJob.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Pinterest, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.pinterest.soundwave.job.definitions;
17 |
18 | import com.pinterest.StatsUtil;
19 | import com.pinterest.aws.AwsClientFactory;
20 | import com.pinterest.config.Configuration;
21 |
22 | import com.amazonaws.regions.Region;
23 | import com.amazonaws.regions.Regions;
24 | import com.amazonaws.services.sqs.AmazonSQSClient;
25 | import com.amazonaws.services.sqs.model.GetQueueAttributesResult;
26 | import com.twitter.ostrich.stats.Stats;
27 | import org.apache.commons.lang.exception.ExceptionUtils;
28 | import org.slf4j.Logger;
29 | import org.slf4j.LoggerFactory;
30 |
31 | import java.util.Arrays;
32 | import java.util.Map;
33 | import java.util.concurrent.Callable;
34 |
35 | public class HealthCheckJob implements Callable {
36 |
37 | private static final Logger logger = LoggerFactory.getLogger(HealthCheckJob.class);
38 | private static final String QUEUELENGTHATTR = "ApproximateNumberOfMessages";
39 | private static final String QUEUEINVISIBLEATTR = "ApproximateNumberOfMessagesNotVisible";
40 | private final AmazonSQSClient sqsClient;
41 | private final String queueUrl;
42 |
43 | public HealthCheckJob() {
44 | sqsClient = AwsClientFactory.createSQSClient(Region.getRegion(Regions.US_EAST_1));
45 | queueUrl =
46 | Configuration.getProperties().getString("update_queue");
47 | }
48 |
49 | @Override
50 | public Boolean call() throws Exception {
51 | checkQueueLength();
52 | return true;
53 | }
54 |
55 | private void checkQueueLength() {
56 | try {
57 | GetQueueAttributesResult
58 | result =
59 | sqsClient.getQueueAttributes(queueUrl, Arrays.asList(QUEUELENGTHATTR,
60 | QUEUEINVISIBLEATTR));
61 | Map attrs = result.getAttributes();
62 |
63 | if (attrs.containsKey(QUEUELENGTHATTR)) {
64 | Stats.addMetric(StatsUtil.getStatsName("healthcheck", "ec2queue_length"),
65 | Integer.parseInt(attrs.get(QUEUELENGTHATTR)));
66 | logger.info("Ec2 queue length is {}", attrs.get(QUEUELENGTHATTR));
67 | }
68 |
69 | if (attrs.containsKey(QUEUEINVISIBLEATTR)) {
70 | Stats.addMetric(StatsUtil.getStatsName("healthcheck", "ec2queue_in_processing"),
71 | Integer.parseInt(attrs.get("ApproximateNumberOfMessagesNotVisible")));
72 | logger.info("Ec2 queue in processing length is {}", attrs.get(QUEUEINVISIBLEATTR));
73 | }
74 |
75 | } catch (Exception ex) {
76 | logger.warn(ExceptionUtils.getRootCauseMessage(ex));
77 | logger.warn(ExceptionUtils.getFullStackTrace(ex));
78 | }
79 |
80 | }
81 | }
82 |
--------------------------------------------------------------------------------