├── .build-jdk11 ├── .blazar-enabled ├── .prettier-java-enabled ├── BaragonService ├── .build-executable ├── src │ ├── test │ │ └── resources │ │ │ └── configs │ │ │ ├── just_a_string.yaml │ │ │ ├── purgeCache-alternate.yaml │ │ │ ├── purgeCache.yaml │ │ │ ├── default.yaml │ │ │ └── override.yaml │ └── main │ │ ├── java │ │ └── com │ │ │ └── hubspot │ │ │ └── baragon │ │ │ └── service │ │ │ ├── elb │ │ │ └── RegisterInstanceResult.java │ │ │ ├── edgecache │ │ │ ├── EdgeCache.java │ │ │ └── cloudflare │ │ │ │ ├── client │ │ │ │ ├── CloudflareClientException.java │ │ │ │ └── models │ │ │ │ │ ├── CloudflareListZonesResponse.java │ │ │ │ │ ├── CloudflarePurgeCacheResponse.java │ │ │ │ │ ├── CloudflareListDnsRecordsResponse.java │ │ │ │ │ ├── CloudflarePurgeCacheResult.java │ │ │ │ │ ├── CloudflareZone.java │ │ │ │ │ ├── CloudflareDnsRecord.java │ │ │ │ │ ├── CloudflarePurgeRequest.java │ │ │ │ │ └── CloudflareError.java │ │ │ │ └── EdgeCacheClass.java │ │ │ ├── exceptions │ │ │ ├── NoMatchingElbForVpcException.java │ │ │ ├── BaragonNotFoundException.java │ │ │ ├── BaragonWebException.java │ │ │ ├── NotifyingUncaughtExceptionManager.java │ │ │ └── NotifyingExceptionMapper.java │ │ │ ├── listeners │ │ │ └── AbstractLatchListener.java │ │ │ ├── config │ │ │ ├── BaragonServiceDWSettings.java │ │ │ ├── SentryConfiguration.java │ │ │ ├── UIConfiguration.java │ │ │ ├── PurgeCacheConfiguration.java │ │ │ ├── GoogleCloudConfiguration.java │ │ │ ├── BaragonWorkerConfiguration.java │ │ │ └── EdgeCacheConfiguration.java │ │ │ ├── resources │ │ │ ├── MetricsResource.java │ │ │ ├── WorkerResource.java │ │ │ ├── BaragonResourcesModule.java │ │ │ ├── StatusResource.java │ │ │ ├── PurgeCacheResource.java │ │ │ ├── RenderedConfigsResource.java │ │ │ ├── UIResource.java │ │ │ └── AliasesResource.java │ │ │ ├── managed │ │ │ └── BaragonExceptionNotifierManaged.java │ │ │ ├── healthcheck │ │ │ └── ZooKeeperHealthcheck.java │ │ │ └── worker │ │ │ └── BaragonElbSyncWorker.java │ │ └── docker │ │ ├── baragon.yaml │ │ └── start.sh └── .blazar.yaml ├── BaragonAgentService ├── .build-executable ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── hubspot │ │ │ └── baragon │ │ │ └── agent │ │ │ ├── models │ │ │ └── FilePathFormatType.java │ │ │ ├── resources │ │ │ ├── BargonAgentResourcesModule.java │ │ │ ├── MetricsResource.java │ │ │ ├── PurgeCacheResource.java │ │ │ ├── BatchRequestResource.java │ │ │ └── RenderedConfigsResource.java │ │ │ ├── handlebars │ │ │ ├── IfContainedInHelperSource.java │ │ │ ├── ToNginxVarHelper.java │ │ │ ├── IfEqualHelperSource.java │ │ │ ├── CurrentRackIsPresentHelper.java │ │ │ └── FirstOfHelper.java │ │ │ ├── ServerProvider.java │ │ │ ├── healthcheck │ │ │ ├── LoadBalancerHealthcheck.java │ │ │ ├── ConfigChecker.java │ │ │ └── ZooKeeperHealthcheck.java │ │ │ ├── config │ │ │ ├── TemplateConfiguration.java │ │ │ └── TestingConfiguration.java │ │ │ └── workers │ │ │ └── AgentHeartbeatWorker.java │ │ └── docker │ │ └── start.sh └── .blazar.yaml ├── BaragonUI ├── app │ ├── assets │ │ └── static │ │ │ ├── images │ │ │ ├── .gitkeep │ │ │ └── favicon.ico │ │ │ └── css │ │ │ ├── select2.png │ │ │ ├── select2x2.png │ │ │ └── select2-spinner.gif │ ├── styles │ │ ├── index.styl │ │ ├── apiPrompts.styl │ │ ├── stylus │ │ │ ├── panels.styl │ │ │ ├── apiRootPrompt.styl │ │ │ ├── tableSubviewButtons.styl │ │ │ ├── pageNotFound.styl │ │ │ ├── color-helpers.styl │ │ │ ├── file-browser.styl │ │ │ ├── jsonModal.styl │ │ │ ├── slaveUsage.styl │ │ │ ├── breadcrumbs.styl │ │ │ ├── helpers.styl │ │ │ ├── vendorOverrides.styl │ │ │ ├── layout.styl │ │ │ ├── bootstrap-tweaks.styl │ │ │ ├── searchInput.styl │ │ │ ├── requestsFilter.styl │ │ │ ├── colors.styl │ │ │ ├── charts.styl │ │ │ ├── select2-tweaks.styl │ │ │ ├── lists.styl │ │ │ ├── reactComponents.styl │ │ │ ├── requestsTasksTable.styl │ │ │ ├── forms.styl │ │ │ ├── globalSearch.styl │ │ │ ├── definitionList.styl │ │ │ └── loader.styl │ │ ├── scss │ │ │ ├── duration-input.scss │ │ │ ├── ui-table.scss │ │ │ └── ansi-log-styles.scss │ │ └── index.scss │ ├── actions │ │ ├── ui │ │ │ ├── elbs.es6 │ │ │ ├── groups.es6 │ │ │ ├── services.es6 │ │ │ ├── requestDetail.es6 │ │ │ ├── loadBalancers.es6 │ │ │ ├── albs.es6 │ │ │ ├── elbDetail.es6 │ │ │ ├── serviceDetail.es6 │ │ │ ├── targetGroupDetail.es6 │ │ │ ├── status.es6 │ │ │ ├── groupDetail.es6 │ │ │ └── albDetail.es6 │ │ └── api │ │ │ ├── status.es6 │ │ │ ├── workers.es6 │ │ │ ├── requests.es6 │ │ │ ├── elbs.es6 │ │ │ └── services.es6 │ ├── reducers │ │ ├── ui │ │ │ ├── index.es6 │ │ │ └── form.es6 │ │ ├── index.es6 │ │ └── api │ │ │ ├── keyed.es6 │ │ │ └── base.es6 │ ├── components │ │ ├── common │ │ │ ├── modal │ │ │ │ ├── ModalWrapper.es6 │ │ │ │ └── FormModalButton.jsx │ │ │ ├── formItems │ │ │ │ ├── ReduxSelect.jsx │ │ │ │ └── formGroups │ │ │ │ │ ├── CheckboxFormGroup.jsx │ │ │ │ │ ├── MultiInputFormGroup.jsx │ │ │ │ │ ├── MapInputFormGroup.jsx │ │ │ │ │ └── TextFormGroup.jsx │ │ │ ├── Application.jsx │ │ │ ├── NotFound.jsx │ │ │ ├── modalButtons │ │ │ │ ├── EnableEditButton.jsx │ │ │ │ ├── DisableEditButton.jsx │ │ │ │ ├── DisableEditModal.jsx │ │ │ │ ├── ReloadServiceButton.jsx │ │ │ │ ├── AddTrafficSourceButton.jsx │ │ │ │ ├── DeleteServiceButton.jsx │ │ │ │ ├── ReloadServiceModal.jsx │ │ │ │ ├── PurgeCacheButton.jsx │ │ │ │ ├── PurgeCacheModal.jsx │ │ │ │ ├── ModifyCountButton.jsx │ │ │ │ ├── RemoveBasePathButton.jsx │ │ │ │ ├── RemoveKnownAgentButton.jsx │ │ │ │ ├── EnableEditModal.jsx │ │ │ │ ├── RemoveTrafficSourceButton.jsx │ │ │ │ ├── EditHealthCheckButton.jsx │ │ │ │ ├── RemoveUpstreamButton.jsx │ │ │ │ ├── RemoveBasePathModal.jsx │ │ │ │ ├── UpdateInstanceButton.jsx │ │ │ │ ├── RemoveUpstreamsButton.jsx │ │ │ │ └── RemoveKnownAgentModal.jsx │ │ │ ├── Title.jsx │ │ │ ├── DetailGroup.jsx │ │ │ └── table │ │ │ │ └── Column.jsx │ │ ├── targetGroupDetail │ │ │ └── DetailItem.jsx │ │ ├── requestDetail │ │ │ ├── OriginalRequestPanel.jsx │ │ │ └── SummaryPanel.jsx │ │ ├── serviceDetail │ │ │ ├── OwnersPanel.jsx │ │ │ ├── LoadBalancersPanel.jsx │ │ │ └── DetailHeader.jsx │ │ ├── status │ │ │ ├── RequestSearch.jsx │ │ │ └── PendingRequests.jsx │ │ ├── groupDetail │ │ │ ├── GroupDomains.jsx │ │ │ ├── GroupAgents.jsx │ │ │ └── GroupTitleBar.jsx │ │ └── elbDetail │ │ │ └── HealthCheckPanel.jsx │ └── store.es6 ├── .gitignore ├── test │ └── index.test.js ├── .blazar.yaml ├── webpack.config.js ├── .babelrc └── .eslintrc.json ├── docker ├── nginx │ ├── conf.d │ │ ├── baragon.conf │ │ └── vhost.conf │ ├── start.sh │ └── nginx.conf └── supervisor │ ├── supervisord.conf │ └── conf.d │ ├── baragon.conf │ └── nginx.conf ├── .blazar.yaml ├── CHANGES.md ├── BaragonClient ├── .blazar.yaml └── src │ ├── main │ └── java │ │ └── com │ │ └── hubspot │ │ └── baragon │ │ └── client │ │ ├── ProviderUtils.java │ │ └── BaragonClientException.java │ └── test │ └── java │ └── com │ └── hubspot │ └── baragon │ └── client │ └── BaragonServiceClientModuleTest.java ├── BaragonCore ├── .blazar.yaml ├── src │ └── main │ │ └── java │ │ └── com │ │ └── hubspot │ │ └── baragon │ │ ├── models │ │ ├── RegisterBy.java │ │ ├── TrafficSourceState.java │ │ ├── TrafficSourceType.java │ │ ├── AgentRequestType.java │ │ ├── BaragonAgentState.java │ │ ├── AgentRequestsStatus.java │ │ ├── RequestAction.java │ │ ├── BaragonRequestState.java │ │ ├── InternalRequestStates.java │ │ └── BaragonRequestKey.java │ │ └── exceptions │ │ ├── InvalidConfigException.java │ │ ├── ReapplyFailedException.java │ │ ├── MissingTemplateException.java │ │ ├── InvalidUpstreamsException.java │ │ ├── WorkerLimitReachedException.java │ │ ├── InvalidRequestActionException.java │ │ ├── AgentServiceNotifyException.java │ │ ├── InvalidAgentMetadataStringException.java │ │ ├── LockTimeoutException.java │ │ ├── RequestAlreadyEnqueuedException.java │ │ └── LbAdapterExecuteException.java └── pom.xml ├── BaragonData ├── .blazar.yaml └── src │ ├── main │ └── java │ │ └── com │ │ └── hubspot │ │ └── baragon │ │ ├── auth │ │ ├── NoAuth.java │ │ ├── BaragonAuthBundle.java │ │ ├── BaragonAuthFilter.java │ │ └── BaragonAuthFeature.java │ │ ├── config │ │ └── AuthConfiguration.java │ │ ├── data │ │ ├── BaragonConnectionStateListener.java │ │ ├── BaragonZkMetaDatastore.java │ │ └── BaragonAliasDatastore.java │ │ ├── managers │ │ └── BaragonAuthManager.java │ │ ├── migrations │ │ ├── ZkDataMigration.java │ │ └── ServiceDomainsMigration.java │ │ └── cache │ │ ├── CachedBaragonState.java │ │ └── BaragonStateCache.java │ └── test │ └── java │ └── com │ └── hubspot │ └── baragon │ └── auth │ ├── AuthFilterTestModule.java │ └── BaragonAuthFilterTest.java ├── BaragonWatcher ├── .blazar.yaml └── src │ └── main │ └── java │ └── com │ └── hubspot │ └── baragon │ └── watcher │ ├── BaragonStateListener.java │ ├── BaragonStateFetcher.java │ └── Baragon.java ├── BaragonServiceIntegrationTests ├── .blazar.yaml └── src │ └── main │ └── java │ └── com │ └── hubspot │ └── baragon │ └── BaragonServiceIntegrationTests.java ├── .travis.yml ├── cookbook ├── README.md └── CHANGELOG.md ├── Docs ├── config │ └── config.md ├── managing_baragon.md ├── about │ └── basics.md └── managing │ ├── basepaths.md │ └── terminology.md ├── .bookignore ├── publish_gitbook.sh ├── .gitignore ├── book.json ├── Dockerfile ├── SUMMARY.md ├── docker_release.sh ├── Dockerfile-aurora ├── docker-compose.yml ├── compose-dev.yml └── intro.md /.build-jdk11: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.blazar-enabled: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettier-java-enabled: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /BaragonService/.build-executable: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /BaragonAgentService/.build-executable: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /BaragonUI/app/assets/static/images/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /BaragonUI/.gitignore: -------------------------------------------------------------------------------- 1 | node 2 | /dist 3 | /target 4 | -------------------------------------------------------------------------------- /BaragonUI/test/index.test.js: -------------------------------------------------------------------------------- 1 | import 'core-js/es6'; -------------------------------------------------------------------------------- /BaragonUI/app/styles/index.styl: -------------------------------------------------------------------------------- 1 | @require 'stylus/*' 2 | -------------------------------------------------------------------------------- /docker/nginx/conf.d/baragon.conf: -------------------------------------------------------------------------------- 1 | include /etc/nginx/conf.d/upstreams/*.conf; -------------------------------------------------------------------------------- /.blazar.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | SET_VERSION_OVERRIDE: "0.10.0-$GIT_BRANCH-SNAPSHOT" 3 | -------------------------------------------------------------------------------- /BaragonService/src/test/resources/configs/just_a_string.yaml: -------------------------------------------------------------------------------- 1 | "no object contents" 2 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## 0.1.0 - Starting to do proper releases. 2 | 3 | - @tpetr: everything. 4 | -------------------------------------------------------------------------------- /BaragonClient/.blazar.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | SET_VERSION_OVERRIDE: "0.10.0-$GIT_BRANCH-SNAPSHOT" 3 | -------------------------------------------------------------------------------- /BaragonCore/.blazar.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | SET_VERSION_OVERRIDE: "0.10.0-$GIT_BRANCH-SNAPSHOT" 3 | -------------------------------------------------------------------------------- /BaragonData/.blazar.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | SET_VERSION_OVERRIDE: "0.10.0-$GIT_BRANCH-SNAPSHOT" 3 | -------------------------------------------------------------------------------- /BaragonUI/.blazar.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | SET_VERSION_OVERRIDE: "0.10.0-$GIT_BRANCH-SNAPSHOT" 3 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/apiPrompts.styl: -------------------------------------------------------------------------------- 1 | #api-prompt-message 2 | p 3 | margin-bottom 1.2em 4 | -------------------------------------------------------------------------------- /BaragonWatcher/.blazar.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | SET_VERSION_OVERRIDE: "0.10.0-$GIT_BRANCH-SNAPSHOT" 3 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/panels.styl: -------------------------------------------------------------------------------- 1 | 2 | .panel-heading--narrow 3 | h1, h2, h3, h4, h5 4 | margin: 0 -------------------------------------------------------------------------------- /BaragonServiceIntegrationTests/.blazar.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | SET_VERSION_OVERRIDE: "0.10.0-$GIT_BRANCH-SNAPSHOT" 3 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/apiRootPrompt.styl: -------------------------------------------------------------------------------- 1 | #api-root-prompt-message 2 | 3 | p 4 | margin-bottom 1.2em 5 | -------------------------------------------------------------------------------- /BaragonUI/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./make-webpack-config')({ 2 | isDebug: false 3 | }); 4 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/tableSubviewButtons.styl: -------------------------------------------------------------------------------- 1 | .table-subview-buttons 2 | text-align center 3 | 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | script: mvn -B -q -DskipBaragonWebUI verify 4 | cache: 5 | directories: 6 | - $HOME/.m2 7 | -------------------------------------------------------------------------------- /docker/supervisor/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | 4 | [include] 5 | files = /etc/supervisor/conf.d/*.conf 6 | -------------------------------------------------------------------------------- /BaragonUI/app/assets/static/css/select2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HubSpot/Baragon/HEAD/BaragonUI/app/assets/static/css/select2.png -------------------------------------------------------------------------------- /BaragonUI/app/assets/static/css/select2x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HubSpot/Baragon/HEAD/BaragonUI/app/assets/static/css/select2x2.png -------------------------------------------------------------------------------- /BaragonUI/app/assets/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HubSpot/Baragon/HEAD/BaragonUI/app/assets/static/images/favicon.ico -------------------------------------------------------------------------------- /BaragonUI/app/assets/static/css/select2-spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HubSpot/Baragon/HEAD/BaragonUI/app/assets/static/css/select2-spinner.gif -------------------------------------------------------------------------------- /BaragonUI/app/styles/scss/duration-input.scss: -------------------------------------------------------------------------------- 1 | 2 | .duration-input { 3 | & label { 4 | font-weight: 400; 5 | margin: 0px 5px; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/pageNotFound.styl: -------------------------------------------------------------------------------- 1 | #page-not-found-wrapper 2 | position fixed 3 | top 0 4 | left 0 5 | right 0 6 | bottom 0 -------------------------------------------------------------------------------- /cookbook/README.md: -------------------------------------------------------------------------------- 1 | # baragon-cookbook 2 | 3 | This cookbook has moved to [evertrue/baragon-cookbook](https://github.com/evertrue/baragon-cookbook). 4 | -------------------------------------------------------------------------------- /BaragonService/src/test/resources/configs/purgeCache-alternate.yaml: -------------------------------------------------------------------------------- 1 | purgeCache: 2 | enabledTemplates: 3 | - static_routes 4 | - static_routes_akamai -------------------------------------------------------------------------------- /BaragonUI/app/actions/ui/elbs.es6: -------------------------------------------------------------------------------- 1 | import { FetchElbs } from '../api/elbs'; 2 | 3 | export const refresh = () => (dispatch) => 4 | dispatch(FetchElbs.trigger()); 5 | -------------------------------------------------------------------------------- /BaragonUI/app/reducers/ui/index.es6: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import form from './form'; 3 | 4 | export default combineReducers({ 5 | form 6 | }); 7 | -------------------------------------------------------------------------------- /BaragonService/.blazar.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | MAVEN_ARGS: "-Dbaragon.jar.name.format=\\${project.artifactId} -Dgpg.skip=true" 3 | SET_VERSION_OVERRIDE: "0.10.0-$GIT_BRANCH-SNAPSHOT" 4 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/models/RegisterBy.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.models; 2 | 3 | public enum RegisterBy { 4 | PRIVATE_IP, 5 | INSTANCE_ID 6 | } 7 | -------------------------------------------------------------------------------- /docker/nginx/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Starting nginx on port ${NGINX_PORT:=80}" 3 | /bin/sed -i "s/NGINX_PORT_PLACEHOLDER/${NGINX_PORT:=80}/" /etc/nginx/conf.d/vhost.conf 4 | nginx -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/models/TrafficSourceState.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.models; 2 | 3 | public enum TrafficSourceState { 4 | DONE, 5 | PENDING, 6 | ERROR 7 | } 8 | -------------------------------------------------------------------------------- /BaragonService/src/test/resources/configs/purgeCache.yaml: -------------------------------------------------------------------------------- 1 | purgeCache: 2 | enabledTemplates: 3 | - static_routes 4 | - static_routes_akamai 5 | excludedServiceIds: 6 | - excludedServiceId -------------------------------------------------------------------------------- /BaragonUI/app/actions/ui/groups.es6: -------------------------------------------------------------------------------- 1 | import { FetchBaragonGroups } from '../../actions/api/groups'; 2 | 3 | export const refresh = () => (dispatch) => 4 | dispatch(FetchBaragonGroups.trigger()); 5 | -------------------------------------------------------------------------------- /BaragonUI/app/actions/ui/services.es6: -------------------------------------------------------------------------------- 1 | import { FetchBaragonServices } from '../../actions/api/services'; 2 | 3 | export const refresh = () => (dispatch) => 4 | dispatch(FetchBaragonServices.trigger()) -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/color-helpers.styl: -------------------------------------------------------------------------------- 1 | @import colors 2 | 3 | .color-warning 4 | color: $orange 5 | 6 | .color-success 7 | color: $green 8 | 9 | .color-error 10 | color: $red -------------------------------------------------------------------------------- /BaragonUI/app/actions/api/status.es6: -------------------------------------------------------------------------------- 1 | import { buildApiAction } from './base'; 2 | 3 | export const FetchBaragonStatus = buildApiAction( 4 | 'FETCH_BARAGON_STATUS', 5 | {url: '/status/master'} 6 | ); 7 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/file-browser.styl: -------------------------------------------------------------------------------- 1 | .file-browser-header small 2 | display none 3 | 4 | .expanded .file-browser-header small 5 | display inline 6 | 7 | .file-name 8 | margin-left 10px 9 | -------------------------------------------------------------------------------- /docker/supervisor/conf.d/baragon.conf: -------------------------------------------------------------------------------- 1 | [program:baragon] 2 | command="/etc/baragon/start.sh" 3 | stdout_logfile=/dev/stdout 4 | stdout_logfile_maxbytes=0 5 | stderr_logfile=/dev/stdout 6 | stderr_logfile_maxbytes=0 -------------------------------------------------------------------------------- /docker/supervisor/conf.d/nginx.conf: -------------------------------------------------------------------------------- 1 | [program:nginx] 2 | command=/etc/nginx/start.sh 3 | stdout_logfile=/dev/stdout 4 | stdout_logfile_maxbytes=0 5 | stderr_logfile=/dev/stdout 6 | stderr_logfile_maxbytes=0 7 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/models/TrafficSourceType.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.models; 2 | 3 | public enum TrafficSourceType { 4 | CLASSIC, 5 | ALB_TARGET_GROUP, 6 | NETWORK 7 | } 8 | -------------------------------------------------------------------------------- /Docs/config/config.md: -------------------------------------------------------------------------------- 1 | ## Example Configuration 2 | 3 | ### Baragon Service 4 | 5 | {% include "baragon_service_config.md" %} 6 | 7 | ### Baragon Agent 8 | 9 | {% include "baragon_agent_config.md" %} 10 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/models/AgentRequestType.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.models; 2 | 3 | public enum AgentRequestType { 4 | APPLY, 5 | REVERT, 6 | CANCEL, 7 | PURGE_CACHE 8 | } 9 | -------------------------------------------------------------------------------- /BaragonUI/app/actions/api/workers.es6: -------------------------------------------------------------------------------- 1 | import { buildApiAction } from './base'; 2 | 3 | export const FetchBaragonServiceWorkers = buildApiAction( 4 | 'FETCH_BARAGON_SERVICE_WORKERS', 5 | {url: '/workers'} 6 | ); 7 | -------------------------------------------------------------------------------- /.bookignore: -------------------------------------------------------------------------------- 1 | cookbook 2 | eclipse 3 | Baragon* 4 | mysql 5 | node_modules 6 | scripts 7 | target 8 | vagrant 9 | .travis.yml 10 | compose-dev.yml 11 | docker-compose.yml 12 | pom.xml 13 | dev 14 | docker* 15 | Docker* -------------------------------------------------------------------------------- /BaragonUI/app/styles/index.scss: -------------------------------------------------------------------------------- 1 | $icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/"; 2 | @import "~bootstrap-sass/assets/stylesheets/bootstrap"; 3 | 4 | @import "./scss/ui-table"; 5 | @import "./scss/new-tailer"; 6 | -------------------------------------------------------------------------------- /BaragonAgentService/src/main/java/com/hubspot/baragon/agent/models/FilePathFormatType.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.agent.models; 2 | 3 | public enum FilePathFormatType { 4 | NONE, 5 | SERVICE, 6 | DOMAIN_SERVICE 7 | } 8 | -------------------------------------------------------------------------------- /BaragonService/src/test/resources/configs/default.yaml: -------------------------------------------------------------------------------- 1 | zookeeper: 2 | connectTimeoutMillis: 100 3 | retryBaseSleepTimeMilliseconds: 100 4 | zkNamespace: "default-namespace" 5 | agentMaxAttempts: 2 6 | hostname: localhost 7 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/models/BaragonAgentState.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.models; 2 | 3 | public enum BaragonAgentState { 4 | BOOTSTRAPING, 5 | ACCEPTING, 6 | APPLYING, 7 | DISCONNECTED 8 | } 9 | -------------------------------------------------------------------------------- /BaragonService/src/test/resources/configs/override.yaml: -------------------------------------------------------------------------------- 1 | zookeeper: 2 | quorum: "override-quorum" 3 | sessionTimeoutMillis: 100 4 | zkNamespace: "override-namespace" 5 | hostname: "127.0.0.1" 6 | masterAuthKey: "master-auth-key" 7 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/elb/RegisterInstanceResult.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.elb; 2 | 3 | public enum RegisterInstanceResult { 4 | ELB_NO_VPC_FOUND, 5 | ELB_AND_VPC_FOUND, 6 | NOT_FOUND 7 | } 8 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/jsonModal.styl: -------------------------------------------------------------------------------- 1 | .json-modal 2 | padding 0 !important 3 | font-family Consolas, "Liberation Mono", Menlo, Courier, monospace 4 | 5 | .constrained-modal 6 | max-height: calc(100vh - 160px) 7 | overflow: auto 8 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/models/AgentRequestsStatus.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.models; 2 | 3 | public enum AgentRequestsStatus { 4 | WAITING, 5 | SUCCESS, 6 | FAILURE, 7 | RETRY, 8 | INVALID_REQUEST_NOOP 9 | } 10 | -------------------------------------------------------------------------------- /BaragonUI/app/actions/ui/requestDetail.es6: -------------------------------------------------------------------------------- 1 | import { FetchRequestResponse } from '../api/requests'; 2 | 3 | export const refresh = (requestId) => dispatch => { 4 | Promise.all([ 5 | dispatch(FetchRequestResponse.trigger(requestId)), 6 | ]); 7 | }; 8 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/models/RequestAction.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.models; 2 | 3 | public enum RequestAction { 4 | UPDATE, 5 | DELETE, 6 | RELOAD, 7 | REVERT, 8 | GET_RENDERED_CONFIG, 9 | PURGE_CACHE 10 | } 11 | -------------------------------------------------------------------------------- /BaragonUI/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": [ 4 | "transform-runtime", 5 | "transform-react-jsx", 6 | "transform-object-rest-spread", 7 | "transform-class-properties", 8 | "react-hot-loader/babel" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /BaragonUI/app/actions/ui/loadBalancers.es6: -------------------------------------------------------------------------------- 1 | import { refresh as albs } from './albs'; 2 | import { refresh as elbs } from './elbs'; 3 | 4 | export const refresh = () => (dispatch) => 5 | Promise.all([ 6 | albs()(dispatch), 7 | elbs()(dispatch) 8 | ]); 9 | -------------------------------------------------------------------------------- /BaragonAgentService/.blazar.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | MAVEN_ARGS: "-Dbaragon.jar.name.format=\\${project.artifactId} -Dgpg.skip=true" 3 | SET_VERSION_OVERRIDE: "0.10.0-$GIT_BRANCH-SNAPSHOT" 4 | 5 | provides: 6 | - name: BaragonAgentService 7 | version: 0.10.0-SNAPSHOT 8 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/slaveUsage.styl: -------------------------------------------------------------------------------- 1 | .single-slave-btn 2 | padding : 35px 20px 3 | margin-right : 15px 4 | margin-bottom : 15px 5 | display: inline-block 6 | 7 | .slave-usage-detail 8 | white-space : nowrap 9 | padding-left : 20px 10 | padding-right : 5px -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/exceptions/InvalidConfigException.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.exceptions; 2 | 3 | public class InvalidConfigException extends Exception { 4 | 5 | public InvalidConfigException(String output) { 6 | super(output); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/exceptions/ReapplyFailedException.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.exceptions; 2 | 3 | public class ReapplyFailedException extends Exception { 4 | 5 | public ReapplyFailedException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/breadcrumbs.styl: -------------------------------------------------------------------------------- 1 | .breadcrumb 2 | 3 | li 4 | max-width 33% 5 | max-height 1.3em 6 | white-space nowrap 7 | overflow hidden 8 | text-overflow ellipsis 9 | 10 | .pull-right 11 | white-space: nowrap 12 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/exceptions/MissingTemplateException.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.exceptions; 2 | 3 | public class MissingTemplateException extends Exception { 4 | 5 | public MissingTemplateException(String output) { 6 | super(output); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/edgecache/EdgeCache.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.edgecache; 2 | 3 | import com.hubspot.baragon.models.BaragonRequest; 4 | 5 | public interface EdgeCache { 6 | boolean invalidateIfNecessary(BaragonRequest request); 7 | } 8 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/exceptions/InvalidUpstreamsException.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.exceptions; 2 | 3 | public class InvalidUpstreamsException extends Exception { 4 | 5 | public InvalidUpstreamsException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BaragonServiceIntegrationTests/src/main/java/com/hubspot/baragon/BaragonServiceIntegrationTests.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon; 2 | 3 | public class BaragonServiceIntegrationTests { 4 | 5 | // No-op main class to appease the Maven gods. 6 | public static void main(String[] args) {} 7 | } 8 | -------------------------------------------------------------------------------- /BaragonUI/app/reducers/index.es6: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { routerReducer as routing } from 'react-router-redux'; 3 | 4 | import api from './api'; 5 | import ui from './ui'; 6 | 7 | export default combineReducers({ 8 | api, 9 | ui, 10 | routing 11 | }); 12 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/exceptions/WorkerLimitReachedException.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.exceptions; 2 | 3 | public class WorkerLimitReachedException extends Exception { 4 | 5 | public WorkerLimitReachedException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/exceptions/InvalidRequestActionException.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.exceptions; 2 | 3 | public class InvalidRequestActionException extends Exception { 4 | 5 | public InvalidRequestActionException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BaragonUI/app/actions/ui/albs.es6: -------------------------------------------------------------------------------- 1 | import { 2 | FetchTargetGroups, 3 | FetchLoadBalancers 4 | } from '../api/albs'; 5 | 6 | export const refresh = () => (dispatch) => 7 | Promise.all([ 8 | dispatch(FetchTargetGroups.trigger()), 9 | dispatch(FetchLoadBalancers.trigger()), 10 | ]); 11 | -------------------------------------------------------------------------------- /BaragonUI/app/actions/ui/elbDetail.es6: -------------------------------------------------------------------------------- 1 | import { FetchElb, FetchElbInstances } from '../api/elbs'; 2 | 3 | export const refresh = (loadBalancerName) => (dispatch) => 4 | Promise.all([ 5 | dispatch(FetchElb.trigger(loadBalancerName)), 6 | dispatch(FetchElbInstances.trigger(loadBalancerName)), 7 | ]); 8 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/exceptions/NoMatchingElbForVpcException.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.exceptions; 2 | 3 | public class NoMatchingElbForVpcException extends Exception { 4 | 5 | public NoMatchingElbForVpcException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/helpers.styl: -------------------------------------------------------------------------------- 1 | .roomy-right 2 | margin-right: 10px 3 | 4 | .half-roomy-right 5 | margin-right: 5px 6 | 7 | .roomy-left 8 | margin-left: 10px 9 | 10 | .half-roomy-left 11 | margin-left: 5px 12 | 13 | 14 | .page-width-normal 15 | max-width 1200px 16 | margin 0 auto 17 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/scss/ui-table.scss: -------------------------------------------------------------------------------- 1 | table { 2 | th { 3 | &.sortable { 4 | cursor: pointer; 5 | } 6 | 7 | &.column-sorted { 8 | background: #f3f3f3; 9 | 10 | &.column-sorted-desc { 11 | 12 | } 13 | 14 | &.column-sorted-asc { 15 | 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BaragonWatcher/src/main/java/com/hubspot/baragon/watcher/BaragonStateListener.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.watcher; 2 | 3 | import com.hubspot.baragon.models.BaragonServiceState; 4 | import java.util.Collection; 5 | 6 | public interface BaragonStateListener { 7 | void stateChanged(Collection newState); 8 | } 9 | -------------------------------------------------------------------------------- /cookbook/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Baragon cookbook CHANGELOG 2 | 3 | ## v1.0.1 (2015-01-21) 4 | 5 | * Clean up Berksfile 6 | - Use proper release of `zookeeper` cookbook instead of GitHub release/tag 7 | * Add `supports` statement to metadata for Supermarket 8 | 9 | ## v1.0.0 (2015-01-18) 10 | 11 | * Initial release of baragon 12 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/listeners/AbstractLatchListener.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.listeners; 2 | 3 | import org.apache.curator.framework.recipes.leader.LeaderLatchListener; 4 | 5 | public abstract class AbstractLatchListener implements LeaderLatchListener { 6 | 7 | public abstract boolean isEnabled(); 8 | } 9 | -------------------------------------------------------------------------------- /BaragonUI/app/actions/ui/serviceDetail.es6: -------------------------------------------------------------------------------- 1 | import {FetchRenderedConfigs, FetchService} from '../../actions/api/services'; 2 | import { FetchRequestHistory } from '../../actions/api/requests'; 3 | 4 | export const refresh = (serviceId) => (dispatch) => 5 | Promise.all([ 6 | dispatch(FetchService.trigger(serviceId)), 7 | dispatch(FetchRequestHistory.trigger(serviceId)), 8 | ]); 9 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/exceptions/AgentServiceNotifyException.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.exceptions; 2 | 3 | public class AgentServiceNotifyException extends Exception { 4 | 5 | public AgentServiceNotifyException(String message) { 6 | super(message); 7 | } 8 | 9 | public AgentServiceNotifyException(Exception cause) { 10 | super(cause); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modal/ModalWrapper.es6: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const getClickComponent = (component, doFirst = (() => Promise.resolve())) => ( 4 | React.Children.map(component.props.children, child => ( 5 | React.cloneElement(child, { 6 | onClick: () => doFirst().then(() => component.refs.modal.getWrappedInstance().show()) 7 | }) 8 | )) 9 | ); 10 | -------------------------------------------------------------------------------- /BaragonClient/src/main/java/com/hubspot/baragon/client/ProviderUtils.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.client; 2 | 3 | import javax.inject.Provider; 4 | 5 | public class ProviderUtils { 6 | 7 | public static Provider of(final T instance) { 8 | return new Provider() { 9 | 10 | @Override 11 | public T get() { 12 | return instance; 13 | } 14 | }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BaragonData/src/main/java/com/hubspot/baragon/auth/NoAuth.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.auth; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target({ ElementType.TYPE, ElementType.METHOD }) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface NoAuth { 11 | } 12 | -------------------------------------------------------------------------------- /BaragonUI/app/actions/ui/targetGroupDetail.es6: -------------------------------------------------------------------------------- 1 | import { 2 | FetchTargetGroup, 3 | FetchTargetGroupTargets, 4 | FetchLoadBalancers, 5 | } from '../api/albs'; 6 | 7 | export const refresh = (groupName) => (dispatch) => 8 | Promise.all([ 9 | dispatch(FetchTargetGroup.trigger(groupName)), 10 | dispatch(FetchTargetGroupTargets.trigger(groupName)), 11 | dispatch(FetchLoadBalancers.trigger()), 12 | ]); 13 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/vendorOverrides.styl: -------------------------------------------------------------------------------- 1 | ul 2 | &.messenger-theme-air 3 | -moz-user-select: all 4 | -webkit-user-select: all 5 | -o-user-select: all 6 | user-select: all 7 | 8 | .react-tagsinput 9 | border none 10 | padding 0 11 | 12 | .react-tagsinput-tag 13 | background-color #F2F9FC 14 | border-radius 2px 15 | border 1px solid darken(#F2F9FC, 25%) 16 | color darken(#F2F9FC, 70%) 17 | -------------------------------------------------------------------------------- /publish_gitbook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | sed -i '' 's/"-sharing",/"-sharing", "-livereload",/g' book.json 6 | gitbook install 7 | gitbook build 8 | cp BaragonUI/app/assets/static/images/favicon.ico _book/gitbook/images/favicon.ico 9 | cd _book 10 | git init 11 | git add . 12 | git commit -m "update gitbook from master branch docs" 13 | git push --force --quiet git@github.com:HubSpot/Baragon.git master:gh-pages -------------------------------------------------------------------------------- /BaragonUI/app/components/common/formItems/ReduxSelect.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Select from 'react-select'; 3 | 4 | // Wrapper for react-select for use with redux form. Needs to override onBlur of react-select 5 | // More info: https://github.com/erikras/redux-form/issues/82 6 | export default (props) => { 7 | return ( 8 | 37 | 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/formItems/formGroups/CheckboxFormGroup.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import { FormGroup, Checkbox } from 'react-bootstrap/lib'; 3 | 4 | const CheckboxFormGroup = (props) => { 5 | return ( 6 | 7 | props.onChange(!props.checked)} 9 | checked={props.checked || false} 10 | inline={true} 11 | disabled={props.disabled} 12 | > 13 | {props.disabled && 14 |
{props.label} {props.tooltipText}
|| 15 | {props.label}} 16 |
17 |
18 | ); 19 | }; 20 | 21 | CheckboxFormGroup.propTypes = { 22 | id: PropTypes.string.isRequired, 23 | label: PropTypes.string.isRequired, 24 | onChange: PropTypes.func.isRequired, 25 | checked: PropTypes.bool, 26 | disabled: PropTypes.bool, 27 | hasTooltip: PropTypes.bool, 28 | tooltipText: PropTypes.string 29 | }; 30 | 31 | export default CheckboxFormGroup; 32 | -------------------------------------------------------------------------------- /BaragonAgentService/src/main/java/com/hubspot/baragon/agent/healthcheck/LoadBalancerHealthcheck.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.agent.healthcheck; 2 | 3 | import com.codahale.metrics.health.HealthCheck; 4 | import com.google.inject.Inject; 5 | import com.google.inject.Singleton; 6 | import com.hubspot.baragon.agent.lbs.LocalLbAdapter; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | @Singleton 11 | public class LoadBalancerHealthcheck extends HealthCheck { 12 | private static final Logger LOG = LoggerFactory.getLogger( 13 | LoadBalancerHealthcheck.class 14 | ); 15 | 16 | private final LocalLbAdapter adapter; 17 | 18 | @Inject 19 | public LoadBalancerHealthcheck(LocalLbAdapter adapter) { 20 | this.adapter = adapter; 21 | } 22 | 23 | @Override 24 | protected Result check() throws Exception { 25 | try { 26 | adapter.checkConfigs(); 27 | return Result.healthy(); 28 | } catch (Exception e) { 29 | LOG.warn("Healthcheck failed: " + e.getMessage()); 30 | return Result.unhealthy(e); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /BaragonClient/src/test/java/com/hubspot/baragon/client/BaragonServiceClientModuleTest.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.client; 2 | 3 | import com.google.inject.AbstractModule; 4 | import com.google.inject.Guice; 5 | import com.google.inject.Inject; 6 | import com.google.inject.Injector; 7 | import com.google.inject.Stage; 8 | import java.util.Collections; 9 | import org.junit.jupiter.api.Test; 10 | 11 | public class BaragonServiceClientModuleTest { 12 | @Inject 13 | BaragonServiceClient client; 14 | 15 | @Test 16 | public void testModuleWithHosts() { 17 | final Injector injector = Guice.createInjector( 18 | Stage.PRODUCTION, 19 | new GuiceDisableModule(), 20 | new BaragonClientModule(Collections.singletonList("http://example.com")) 21 | ); 22 | 23 | injector.injectMembers(this); 24 | } 25 | 26 | private static class GuiceDisableModule extends AbstractModule { 27 | 28 | @Override 29 | protected void configure() { 30 | binder().disableCircularProxies(); 31 | binder().requireExplicitBindings(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /BaragonUI/app/components/requestDetail/SummaryPanel.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router'; 3 | 4 | const SummaryPanel = ({serviceId, message}) => { 5 | return ( 6 |
7 |
8 |
9 |

Summary

10 |
11 |
12 |
13 |
14 |

Service ID:

15 |

{serviceId}

16 |
17 |
18 |
19 |
20 |

Message:

{message}

21 |
22 |
23 |
24 |
25 |
26 | ); 27 | }; 28 | 29 | SummaryPanel.propTypes = { 30 | serviceId: React.PropTypes.string, 31 | message: React.PropTypes.string, 32 | }; 33 | 34 | export default SummaryPanel; 35 | -------------------------------------------------------------------------------- /BaragonData/src/main/java/com/hubspot/baragon/auth/BaragonAuthFilter.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.auth; 2 | 3 | import com.google.inject.Inject; 4 | import com.hubspot.baragon.managers.BaragonAuthManager; 5 | import java.io.IOException; 6 | import javax.ws.rs.WebApplicationException; 7 | import javax.ws.rs.container.ContainerRequestContext; 8 | import javax.ws.rs.container.ContainerRequestFilter; 9 | import javax.ws.rs.core.Response; 10 | import javax.ws.rs.core.Response.Status; 11 | 12 | public class BaragonAuthFilter implements ContainerRequestFilter { 13 | private final BaragonAuthManager authManager; 14 | 15 | @Inject 16 | public BaragonAuthFilter(BaragonAuthManager authManager) { 17 | this.authManager = authManager; 18 | } 19 | 20 | @Override 21 | public void filter(ContainerRequestContext request) throws IOException { 22 | String authKey = request.getUriInfo().getQueryParameters().getFirst("authkey"); 23 | 24 | if (!authManager.isAuthenticated(authKey)) { 25 | throw new WebApplicationException(Response.status(Status.FORBIDDEN).build()); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/exceptions/LbAdapterExecuteException.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.exceptions; 2 | 3 | public class LbAdapterExecuteException extends Exception { 4 | private final String output; 5 | private final Exception executeException; 6 | private final String command; 7 | 8 | public LbAdapterExecuteException( 9 | String output, 10 | Exception executeException, 11 | String command 12 | ) { 13 | super(String.format("%s : %s", command, executeException.getMessage())); 14 | this.output = output; 15 | this.executeException = executeException; 16 | this.command = command; 17 | } 18 | 19 | public LbAdapterExecuteException(String output, String command) { 20 | super(command); 21 | this.output = output; 22 | this.executeException = null; 23 | this.command = command; 24 | } 25 | 26 | public String getOutput() { 27 | return output; 28 | } 29 | 30 | public Exception getExecuteException() { 31 | return executeException; 32 | } 33 | 34 | public String getCommand() { 35 | return command; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/formItems/formGroups/MultiInputFormGroup.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import MultiInput from '../MultiInput'; 3 | import { FormGroup, ControlLabel } from 'react-bootstrap/lib'; 4 | 5 | const MultiInputFormGroup = (props) => ( 6 | 7 | {props.label} 8 | 15 | 16 | ); 17 | 18 | MultiInputFormGroup.propTypes = { 19 | id: PropTypes.string.isRequired, 20 | label: PropTypes.string.isRequired, 21 | onChange: PropTypes.func.isRequired, 22 | placeholder: PropTypes.string, 23 | value: PropTypes.arrayOf(PropTypes.string), 24 | required: PropTypes.bool, 25 | couldHaveFeedback: PropTypes.bool, 26 | errorIndices: PropTypes.arrayOf(PropTypes.number) 27 | }; 28 | 29 | export default MultiInputFormGroup; 30 | -------------------------------------------------------------------------------- /BaragonUI/app/components/groupDetail/GroupDomains.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | import Utils from '../../utils'; 4 | 5 | const domainElement = (domain, key, defaultDomain) => { 6 | if (domain === defaultDomain) { 7 | return ( 8 |
  • 9 | {defaultDomain} 10 | Default 11 |
  • 12 | ); 13 | } else { 14 | return
  • {domain}
  • ; 15 | } 16 | }; 17 | 18 | const GroupDomains = ({domains, defaultDomain}) => { 19 | if (!domains) { 20 | return null; 21 | } 22 | 23 | const domainColumns = Utils.asGroups(domains, 4, (domain, key) => { 24 | return domainElement(domain, key, defaultDomain); 25 | }); 26 | 27 | return ( 28 |
    29 |

    Domains Served

    30 | {domainColumns} 31 |
    32 | ); 33 | }; 34 | 35 | GroupDomains.propTypes = { 36 | domains: PropTypes.array, 37 | defaultDomain: PropTypes.string, 38 | }; 39 | 40 | export default GroupDomains; 41 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/lists.styl: -------------------------------------------------------------------------------- 1 | @import colors 2 | 3 | .link-list 4 | padding 0 5 | ul, li 6 | list-style none 7 | 8 | // Simple Lists 9 | .list-plain 10 | list-style none 11 | padding-left 0 12 | li 13 | margin-bottom 5px 14 | 15 | // List of links in a panel 16 | .list-group--links 17 | border none 18 | 19 | .list-group-item 20 | padding 0 21 | a 22 | display inline-block 23 | &.inactive-link 24 | 25 | &:hover 26 | background inherit 27 | color $base-text 28 | 29 | .list-group--slim 30 | .list-group-item a 31 | padding 5px 15px 32 | 33 | .list-group--plain 34 | .list-group-item 35 | border none 36 | a, li 37 | margin: 5px 0 38 | 39 | .inactive-link, .inactive-link .chart__label 40 | color $grey 41 | text-decoration none 42 | &:hover 43 | text-decoration none 44 | color $grey 45 | cursor default 46 | 47 | .list-large 48 | font-size 18px -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/reactComponents.styl: -------------------------------------------------------------------------------- 1 | @import colors 2 | 3 | .remove-query-param 4 | color $red 5 | 6 | .table-nav-bar 7 | background-color $grey-light 8 | padding 10px 9 | 10 | .current-query-param 11 | margin-left 10px 12 | 13 | .pagination-container 14 | text-align center 15 | 16 | ul.pagination 17 | position relative 18 | 19 | .status-list 20 | list-style-type none 21 | padding-left 0 22 | 23 | li 24 | margin-bottom 5px 25 | 26 | li.waiting 27 | color $grey-medium 28 | 29 | .glyphicon-spin 30 | -webkit-animation spin 1500ms infinite linear; 31 | animation spin 1500ms infinite linear; 32 | 33 | @-webkit-keyframes spin 34 | 0% 35 | -webkit-transform rotate(0deg) 36 | transform rotate(0deg) 37 | 38 | 100% 39 | -webkit-transform rotate(359deg) 40 | transform rotate(359deg) 41 | 42 | @keyframes spin 43 | 0% 44 | -webkit-transform: rotate(0deg) 45 | transform rotate(0deg) 46 | 47 | 100% 48 | -webkit-transform rotate(359deg) 49 | transform rotate(359deg) 50 | 51 | .task-filters 52 | & button.pull-right 53 | margin-left 10px 54 | -------------------------------------------------------------------------------- /BaragonData/src/main/java/com/hubspot/baragon/data/BaragonConnectionStateListener.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.data; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import com.google.inject.name.Named; 6 | import com.hubspot.baragon.BaragonDataModule; 7 | import java.util.concurrent.atomic.AtomicReference; 8 | import org.apache.curator.framework.CuratorFramework; 9 | import org.apache.curator.framework.state.ConnectionState; 10 | import org.apache.curator.framework.state.ConnectionStateListener; 11 | 12 | @Singleton 13 | public class BaragonConnectionStateListener implements ConnectionStateListener { 14 | private final AtomicReference connectionState; 15 | 16 | @Inject 17 | public BaragonConnectionStateListener( 18 | @Named( 19 | BaragonDataModule.BARAGON_ZK_CONNECTION_STATE 20 | ) AtomicReference connectionState 21 | ) { 22 | this.connectionState = connectionState; 23 | } 24 | 25 | @Override 26 | public void stateChanged(CuratorFramework client, ConnectionState newState) { 27 | connectionState.set(newState); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BaragonCore/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.hubspot 7 | Baragon 8 | 0.10.0-SNAPSHOT 9 | 10 | 11 | BaragonCore 12 | 13 | 14 | 15 | com.amazonaws 16 | aws-java-sdk-core 17 | 18 | 19 | com.fasterxml.jackson.core 20 | jackson-annotations 21 | 22 | 23 | com.google.guava 24 | guava 25 | 26 | 27 | javax.validation 28 | validation-api 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/models/InternalRequestStates.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.models; 2 | 3 | public enum InternalRequestStates { 4 | PENDING(false, false), 5 | INVALID_REQUEST_NOOP(false, false), 6 | SEND_APPLY_REQUESTS(true, false), 7 | CHECK_APPLY_RESPONSES(false, true), 8 | COMPLETED(false, false), 9 | FAILED_SEND_REVERT_REQUESTS(true, true), 10 | FAILED_CHECK_REVERT_RESPONSES(false, true), 11 | FAILED_REVERTED(false, false), 12 | FAILED_REVERT_FAILED(false, false), 13 | CANCELLED_SEND_REVERT_REQUESTS(true, true), 14 | CANCELLED_CHECK_REVERT_RESPONSES(false, true), 15 | CANCELLED(false, false), 16 | FAILED_CANCEL_FAILED(false, false), 17 | COMPLETED_POST_APPLY_FAILED(false, false); 18 | 19 | private final boolean requireAgentRequest; 20 | private final boolean inFlight; 21 | 22 | InternalRequestStates(boolean requireAgentRequest, boolean inFlight) { 23 | this.requireAgentRequest = requireAgentRequest; 24 | this.inFlight = inFlight; 25 | } 26 | 27 | public boolean isRequireAgentRequest() { 28 | return requireAgentRequest; 29 | } 30 | 31 | public boolean isInFlight() { 32 | return inFlight; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/requestsTasksTable.styl: -------------------------------------------------------------------------------- 1 | .table-nav-pills 2 | 3 | .separator-pill 4 | margin-right 20px 5 | padding-right 20px 6 | border-right 1px dashed #ccc 7 | 8 | .table-staged 9 | 10 | .centered 11 | text-align center 12 | 13 | th, td 14 | min-width 35px 15 | 16 | th[data-sort-attribute] 17 | cursor pointer 18 | white-space nowrap 19 | overflow hidden 20 | 21 | th[data-sorted="true"] 22 | overflow visible 23 | position relative 24 | z-index 500 25 | background #f3f3f3 26 | 27 | span.glyphicon-chevron-up,span.glyphicon-chevron-down 28 | font-size 10px 29 | line-height 10px 30 | height 19px 31 | width 10px 32 | position absolute 33 | top -4px 34 | left 50% 35 | margin-left -6px 36 | 37 | td 38 | overflow hidden 39 | 40 | .keep-in-check 41 | max-width 300px 42 | white-space nowrap 43 | max-height 1.5em 44 | overflow hidden 45 | text-overflow ellipsis 46 | 47 | .tooltip-inner 48 | max-width: 250px 49 | white-space: pre-wrap 50 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/resources/PurgeCacheResource.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.resources; 2 | 3 | import com.google.inject.Inject; 4 | import com.hubspot.baragon.models.BaragonResponse; 5 | import com.hubspot.baragon.service.managers.PurgeCacheManager; 6 | import com.hubspot.baragon.service.managers.ServiceManager; 7 | import javax.ws.rs.POST; 8 | import javax.ws.rs.Path; 9 | import javax.ws.rs.PathParam; 10 | import javax.ws.rs.Produces; 11 | import javax.ws.rs.core.MediaType; 12 | 13 | @Path("/purgeCache") 14 | @Produces(MediaType.APPLICATION_JSON) 15 | public class PurgeCacheResource { 16 | private final PurgeCacheManager purgeCacheManager; 17 | private final ServiceManager serviceManager; 18 | 19 | @Inject 20 | public PurgeCacheResource( 21 | PurgeCacheManager purgeCacheManager, 22 | ServiceManager serviceManager 23 | ) { 24 | this.purgeCacheManager = purgeCacheManager; 25 | this.serviceManager = serviceManager; 26 | } 27 | 28 | @POST 29 | @Path("/{serviceId}") 30 | public BaragonResponse purgeCacheAsync(@PathParam("serviceId") String serviceId) { 31 | return serviceManager.enqueuePurgeCache(serviceId); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/resources/RenderedConfigsResource.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.resources; 2 | 3 | import com.google.inject.Inject; 4 | import com.hubspot.baragon.auth.NoAuth; 5 | import com.hubspot.baragon.models.BaragonConfigFile; 6 | import com.hubspot.baragon.service.managers.RenderedConfigsManager; 7 | import java.util.List; 8 | import javax.ws.rs.GET; 9 | import javax.ws.rs.Path; 10 | import javax.ws.rs.PathParam; 11 | import javax.ws.rs.Produces; 12 | import javax.ws.rs.core.MediaType; 13 | 14 | @Path("/renderedConfigs") 15 | @Produces(MediaType.APPLICATION_JSON) 16 | public class RenderedConfigsResource { 17 | private final RenderedConfigsManager renderedConfigsManager; 18 | 19 | @Inject 20 | public RenderedConfigsResource(RenderedConfigsManager renderedConfigsManager) { 21 | this.renderedConfigsManager = renderedConfigsManager; 22 | } 23 | 24 | @GET 25 | @NoAuth 26 | @Path("/{serviceId}") 27 | public List getRenderedConfigs( 28 | @PathParam("serviceId") String serviceId 29 | ) 30 | throws Exception { 31 | return renderedConfigsManager.synchronouslySendRenderConfigsRequest(serviceId); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /BaragonData/src/main/java/com/hubspot/baragon/data/BaragonZkMetaDatastore.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.data; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.base.Optional; 5 | import com.google.inject.Inject; 6 | import com.hubspot.baragon.config.ZooKeeperConfiguration; 7 | import org.apache.curator.framework.CuratorFramework; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public class BaragonZkMetaDatastore extends AbstractDataStore { 12 | private static final Logger LOG = LoggerFactory.getLogger(BaragonZkMetaDatastore.class); 13 | 14 | private static final String ZK_DATA_VERSION_PATH = "/zk-data-version"; 15 | 16 | @Inject 17 | public BaragonZkMetaDatastore( 18 | CuratorFramework curatorFramework, 19 | ObjectMapper objectMapper, 20 | ZooKeeperConfiguration zooKeeperConfiguration 21 | ) { 22 | super(curatorFramework, objectMapper, zooKeeperConfiguration); 23 | } 24 | 25 | public Optional getZkDataVersion() { 26 | return readFromZk(ZK_DATA_VERSION_PATH, String.class); 27 | } 28 | 29 | public void setZkDataVersion(String version) { 30 | writeToZk(ZK_DATA_VERSION_PATH, version); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/table/Column.jsx: -------------------------------------------------------------------------------- 1 | import { Component, PropTypes } from 'react'; 2 | 3 | class Column extends Component { 4 | static propTypes = { 5 | id: PropTypes.string.isRequired, // must be unique 6 | label: PropTypes.oneOfType([ 7 | PropTypes.string, 8 | PropTypes.func 9 | ]), 10 | cellData: PropTypes.func, 11 | cellRender: PropTypes.func, 12 | sortable: PropTypes.bool, 13 | sortData: PropTypes.func, // (cellData, object) -> any 14 | sortFunc: PropTypes.func, 15 | className: PropTypes.oneOfType([ 16 | PropTypes.string, 17 | PropTypes.func 18 | ]), 19 | headerClassName: PropTypes.string 20 | }; 21 | 22 | static defaultProps = { 23 | cellData: (rowData) => rowData, 24 | cellRender: (cellData) => cellData, 25 | label: '', 26 | sortable: false, 27 | sortData: (cellData) => cellData, 28 | /* eslint-disable id-length */ // Exception for comparator 29 | sortFunc: (a, b) => { 30 | /* eslint-enable id-length */ 31 | if (a < b) { 32 | return -1; 33 | } 34 | if (a > b) { 35 | return 1; 36 | } 37 | return 0; 38 | } 39 | }; 40 | } 41 | 42 | export default Column; 43 | -------------------------------------------------------------------------------- /BaragonAgentService/src/main/java/com/hubspot/baragon/agent/config/TemplateConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.agent.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import java.util.Map; 6 | import javax.validation.constraints.NotNull; 7 | import org.hibernate.validator.constraints.NotEmpty; 8 | 9 | @JsonIgnoreProperties(ignoreUnknown = true) 10 | public class TemplateConfiguration { 11 | @NotEmpty 12 | @JsonProperty("filename") 13 | private String filename; 14 | 15 | @NotNull 16 | @JsonProperty("template") 17 | private String defaultTemplate; 18 | 19 | @JsonProperty("namedTemplates") 20 | private Map extraTemplates; 21 | 22 | public String getFilename() { 23 | return filename; 24 | } 25 | 26 | public void setFilename(String filename) { 27 | this.filename = filename; 28 | } 29 | 30 | public String getDefaultTemplate() { 31 | return defaultTemplate; 32 | } 33 | 34 | public void setTemplate(String template) { 35 | this.defaultTemplate = template; 36 | } 37 | 38 | public Map getNamedTemplates() { 39 | return extraTemplates; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /BaragonService/src/main/docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ${DOCKER_HOST} ]; then 4 | HOST_AND_PORT=`echo $DOCKER_HOST | awk -F/ '{print $3}'` 5 | DEFAULT_HOSTNAME="${HOST_AND_PORT%:*}" 6 | BARAGON_HOSTNAME="${BARAGON_HOSTNAME:=$DEFAULT_HOSTNAME}" 7 | fi 8 | 9 | DEFAULT_URI_BASE="http://${BARAGON_HOSTNAME:=localhost}:${BARAGON_PORT:=8080}${BARAGON_UI_BASE:=/baragon/v2}" 10 | 11 | [[ ! ${BARAGON_ZK_NAMESPACE:-} ]] || args+=( -Ddw.zookeeper.zkNamespace="$BARAGON_ZK_NAMESPACE" ) 12 | [[ ! ${BARAGON_ZK_QUORUM:-} ]] || args+=( -Ddw.zookeeper.quorum="$BARAGON_ZK_QUORUM" ) 13 | [[ ! ${BARAGON_PORT:-} ]] || args+=( -Ddw.server.connector.port="$BARAGON_PORT" ) 14 | [[ ! ${BARAGON_AUTH_ENABLED:-} ]] || args+=( -Ddw.auth.enabled="$BARAGON_AUTH_ENABLED" ) 15 | [[ ! ${BARAGON_AUTH_KEY:-} ]] || args+=( -Ddw.auth.key="$BARAGON_AUTH_KEY" ) 16 | [[ ! ${BARAGON_HOSTNAME:-} ]] || args+=( -Ddw.hostname="$BARAGON_HOSTNAME" ) 17 | 18 | args+=( -Ddw.ui.baseUrl="${BARAGON_UI_BASE_URL:=$DEFAULT_URI_BASE}" ) 19 | 20 | args+=( -Djava.net.preferIPv4Stack=true ) 21 | args+=( -Xmx512m ) 22 | 23 | CONFIG_FILE="${BARAGON_CONFIG_FILE:=/etc/baragon/baragon.yaml}" 24 | 25 | exec java "${args[@]}" -jar "/etc/baragon/BaragonService.jar" server $CONFIG_FILE -------------------------------------------------------------------------------- /BaragonAgentService/src/main/java/com/hubspot/baragon/agent/resources/PurgeCacheResource.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.agent.resources; 2 | 3 | import com.google.inject.Inject; 4 | import com.hubspot.baragon.agent.managers.AgentRequestManager; 5 | import javax.ws.rs.Consumes; 6 | import javax.ws.rs.POST; 7 | import javax.ws.rs.Path; 8 | import javax.ws.rs.PathParam; 9 | import javax.ws.rs.Produces; 10 | import javax.ws.rs.core.MediaType; 11 | import javax.ws.rs.core.Response; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | @Path("/purgeCache") 16 | @Produces(MediaType.APPLICATION_JSON) 17 | public class PurgeCacheResource { 18 | private static final Logger LOG = LoggerFactory.getLogger(PurgeCacheResource.class); 19 | private final AgentRequestManager agentRequestManager; 20 | 21 | @Inject 22 | public PurgeCacheResource(AgentRequestManager agentRequestManager) { 23 | this.agentRequestManager = agentRequestManager; 24 | } 25 | 26 | @POST 27 | @Consumes(MediaType.APPLICATION_JSON) 28 | @Path("/{serviceId}") 29 | public Response apply(@PathParam("serviceId") String serviceId) 30 | throws InterruptedException { 31 | return agentRequestManager.purgeCache(serviceId); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/DisableEditModal.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import { DisableAuthKey } from '../../../actions/api/auth'; 5 | 6 | import FormModal from '../modal/FormModal'; 7 | 8 | class EnableEditModal extends Component { 9 | static propTypes = { 10 | disableAuth: PropTypes.func.isRequired 11 | }; 12 | 13 | show() { 14 | this.refs.disableEditModal.show(); 15 | } 16 | 17 | render() { 18 | return ( 19 | 26 |

    27 | Continuing will disable edit mode. You will need to click 'Enable Edit' 28 | and enter your auth key to re-enable. 29 |

    30 |
    31 | ); 32 | } 33 | } 34 | 35 | const mapDispatchToProps = (dispatch) => ({ 36 | disableAuth: () => dispatch(DisableAuthKey.trigger()), 37 | }); 38 | 39 | export default connect( 40 | null, 41 | mapDispatchToProps, 42 | null, 43 | { withRef: true } 44 | )(EnableEditModal); 45 | -------------------------------------------------------------------------------- /BaragonUI/app/components/groupDetail/GroupAgents.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | import UITable from '../common/table/UITable'; 4 | import Column from '../common/table/Column'; 5 | import Utils from '../../utils'; 6 | 7 | const GroupAgents = ({agents}) => { 8 | return ( 9 |
    10 |

    Active Agents

    11 | agent.agentId} 14 | paginated={false} 15 | > 16 | ({agent.agentId}) 22 | } 23 | sortable={true} 24 | /> 25 | Utils.maybe(agent, ['ec2', 'instanceId'], "") 31 | } 32 | sortable={true} 33 | /> 34 | 35 |
    36 | ); 37 | }; 38 | 39 | GroupAgents.propTypes = { 40 | agents: PropTypes.array, 41 | }; 42 | 43 | export default GroupAgents; 44 | -------------------------------------------------------------------------------- /BaragonAgentService/src/main/java/com/hubspot/baragon/agent/healthcheck/ConfigChecker.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.agent.healthcheck; 2 | 3 | import com.google.common.base.Optional; 4 | import com.google.inject.Inject; 5 | import com.google.inject.name.Named; 6 | import com.hubspot.baragon.agent.BaragonAgentServiceModule; 7 | import com.hubspot.baragon.agent.lbs.LocalLbAdapter; 8 | import com.hubspot.baragon.exceptions.InvalidConfigException; 9 | import java.util.concurrent.atomic.AtomicReference; 10 | 11 | public class ConfigChecker implements Runnable { 12 | private final LocalLbAdapter adapter; 13 | private final AtomicReference> errorMessage; 14 | 15 | @Inject 16 | public ConfigChecker( 17 | LocalLbAdapter adapter, 18 | @Named( 19 | BaragonAgentServiceModule.CONFIG_ERROR_MESSAGE 20 | ) AtomicReference> errorMessage 21 | ) { 22 | this.adapter = adapter; 23 | this.errorMessage = errorMessage; 24 | } 25 | 26 | @Override 27 | public void run() { 28 | try { 29 | adapter.checkConfigs(); 30 | errorMessage.set(Optional.absent()); 31 | } catch (InvalidConfigException e) { 32 | errorMessage.set(Optional.of(e.getMessage())); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/config/PurgeCacheConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.hubspot.baragon.models.BaragonService; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import javax.validation.constraints.NotNull; 9 | 10 | @JsonIgnoreProperties(ignoreUnknown = true) 11 | public class PurgeCacheConfiguration { 12 | @JsonProperty("enabledTemplates") 13 | @NotNull 14 | private List enabledTemplates = new ArrayList<>(); 15 | 16 | @JsonProperty(value = "excludedServiceIds") 17 | @NotNull 18 | private List excludedServiceIds = new ArrayList<>(); 19 | 20 | public List getEnabledTemplates() { 21 | return enabledTemplates; 22 | } 23 | 24 | public void setEnabledTemplates(List enabledTemplates) { 25 | this.enabledTemplates = enabledTemplates; 26 | } 27 | 28 | public List getExcludedServiceIds() { 29 | return excludedServiceIds; 30 | } 31 | 32 | public void setExcludedServiceIds(List excludedServiceIds) { 33 | this.excludedServiceIds = excludedServiceIds; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Docs/about/basics.md: -------------------------------------------------------------------------------- 1 | ## Baragon Basics 2 | 3 | Baragon is made up of two services: 4 | 5 | - BaragonService -- coordination service 6 | 7 | - BaragonAgentService -- applies changes on the actual load balancer 8 | 9 | When a web service changes (i.e. upstreams added / removed), POST a [BaragonRequest](../api.md#requests) JSON object to BaragonService's `/[contextPath]/request` endpoint like this one: 10 | 11 | ```json 12 | { 13 | "loadBalancerRequestId": "4", 14 | "loadBalancerService": { 15 | "serviceId": "1", 16 | "owners": ["foo"], 17 | "serviceBasePath": "/basepath", 18 | "loadBalancerGroups": ["loadBalancerGroupName"] 19 | }, 20 | "addUpstreams": ["1.1.1.1:80"], 21 | "removeUpstreams": [] 22 | } 23 | ``` 24 | 25 | - `BaragonService` will fan out the update to all `BaragonAgent`s in the specified `loadBalancerGroups` 26 | - `BaragonAgent`s will apply the changes on the load balancer using templates provided in its configuration and report back a Success or Failure to `BaragonService` 27 | - Polling the `BaragonService` request status url (`/[contextPath]/request/{loadBalancerRequestId}`) will indicate the current status of the request 28 | 29 | Check out the [API Docs](../api.md) for additional `BaragonRequest` fields and returned values. -------------------------------------------------------------------------------- /BaragonData/src/main/java/com/hubspot/baragon/auth/BaragonAuthFeature.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.auth; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import com.hubspot.baragon.config.AuthConfiguration; 6 | import javax.ws.rs.container.DynamicFeature; 7 | import javax.ws.rs.container.ResourceInfo; 8 | import javax.ws.rs.core.FeatureContext; 9 | 10 | @Singleton 11 | public class BaragonAuthFeature implements DynamicFeature { 12 | private final BaragonAuthFilter requestFilter; 13 | private final AuthConfiguration authConfiguration; 14 | 15 | @Inject 16 | public BaragonAuthFeature( 17 | BaragonAuthFilter requestFilter, 18 | AuthConfiguration authConfiguration 19 | ) { 20 | this.requestFilter = requestFilter; 21 | this.authConfiguration = authConfiguration; 22 | } 23 | 24 | @Override 25 | public void configure(ResourceInfo resourceInfo, FeatureContext featureContext) { 26 | if (authConfiguration.isEnabled()) { 27 | if ( 28 | resourceInfo.getResourceMethod().getAnnotation(NoAuth.class) == null && 29 | resourceInfo.getResourceClass().getAnnotation(NoAuth.class) == null 30 | ) { 31 | featureContext.register(requestFilter); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/forms.styl: -------------------------------------------------------------------------------- 1 | @import colors 2 | 3 | // Form helpers 4 | 5 | label 6 | &.label-light 7 | font-weight normal 8 | 9 | .select2-container-multi 10 | width 100% 11 | 12 | // mimic bootstrap 13 | .select2-choices 14 | display: block 15 | width: 100% 16 | height: 34px 17 | padding: 5px 3px 0px 18 | font-size: 14px 19 | line-height: 1.42857143 20 | color: #555 21 | background-color: #fff 22 | background-image: none 23 | border: 1px solid #ccc 24 | border-radius: 4px 25 | 26 | // // temporary hack 27 | // .select2-results 28 | // display: none!important 29 | &.label-light 30 | font-weight normal 31 | 32 | .cmd-description 33 | margin-top 10px 34 | font-style oblique 35 | 36 | .check-label 37 | margin-top 15px 38 | 39 | .hidden 40 | display none 41 | 42 | #cmd-confirm 43 | color $green 44 | 45 | .remove-button 46 | position absolute 47 | top 0.75em 48 | right 1.5em 49 | width 2em 50 | height 2em 51 | line-height 1.75em 52 | text-align center 53 | 54 | &:hover 55 | background #fff 56 | border-radius 3px 57 | cursor pointer 58 | 59 | &:after 60 | content '×' 61 | font-size 20px 62 | color #999 63 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/edgecache/cloudflare/client/models/CloudflarePurgeCacheResult.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.edgecache.cloudflare.client.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | @JsonIgnoreProperties(ignoreUnknown = true) 8 | public class CloudflarePurgeCacheResult { 9 | private final String id; 10 | 11 | @JsonCreator 12 | public CloudflarePurgeCacheResult(@JsonProperty("id") String id) { 13 | this.id = id; 14 | } 15 | 16 | public String getId() { 17 | return id; 18 | } 19 | 20 | @Override 21 | public boolean equals(Object o) { 22 | if (this == o) { 23 | return true; 24 | } 25 | if (o == null || getClass() != o.getClass()) { 26 | return false; 27 | } 28 | 29 | CloudflarePurgeCacheResult that = (CloudflarePurgeCacheResult) o; 30 | 31 | return id.equals(that.id); 32 | } 33 | 34 | @Override 35 | public int hashCode() { 36 | return id.hashCode(); 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "CloudflarePurgeCacheResult{" + "id='" + id + '\'' + '}'; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /BaragonData/src/main/java/com/hubspot/baragon/managers/BaragonAuthManager.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.managers; 2 | 3 | import com.google.common.base.Optional; 4 | import com.google.inject.Inject; 5 | import com.google.inject.Singleton; 6 | import com.google.inject.name.Named; 7 | import com.hubspot.baragon.BaragonDataModule; 8 | import com.hubspot.baragon.models.BaragonAuthKey; 9 | import java.util.Map; 10 | import java.util.concurrent.atomic.AtomicReference; 11 | 12 | @Singleton 13 | public class BaragonAuthManager { 14 | private final AtomicReference> authKeys; 15 | 16 | @Inject 17 | public BaragonAuthManager( 18 | @Named( 19 | BaragonDataModule.BARAGON_AUTH_KEY_MAP 20 | ) AtomicReference> authKeys 21 | ) { 22 | this.authKeys = authKeys; 23 | } 24 | 25 | public boolean isAuthenticated(String authKey) { 26 | final Optional maybeAuthKey = Optional.fromNullable( 27 | authKeys.get().get(authKey) 28 | ); 29 | 30 | return ( 31 | maybeAuthKey.isPresent() && 32 | ( 33 | !maybeAuthKey.get().getExpiredAt().isPresent() || 34 | maybeAuthKey.get().getExpiredAt().get() > System.currentTimeMillis() 35 | ) 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Docs/managing/basepaths.md: -------------------------------------------------------------------------------- 1 | ## BasePath Locking and Updating 2 | 3 | When adding a service to Baragon, the service must have an associated `basePath`. Originally built around nginx, this was to ensure that no two services would attempt to be hosted on the same path on the same load balancer (causing an invalid configuration). The base path lock is always associated with a serviceId. Some notes on locking and changing base paths for a service: 4 | 5 | - base paths are locked at the beginning of request processing to avoid conflicts 6 | - If a service is moved off of a load balancer group (ie. changed from group A to group B) the base path lock is released on the old group 7 | - If a base path changes the lock on the old one is released 8 | - To have a new serviceId take over a base path that is currently locked, use the `replaceServiceId` request field. (The alternative to this is to make a request to remove the previous service than a request to add the new service. This is not an instant switch and could cause down time) 9 | - Example: 10 | - Service `A` is at base path `/test` 11 | - Make a request for service `B` with `replaceServiceId` set to `A` 12 | - Service `A`'s configs are removed, service `B`'s configs are added, base path `/test` is now associated with service `B` -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/config/GoogleCloudConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.config; 2 | 3 | public class GoogleCloudConfiguration { 4 | private boolean enabled = false; 5 | private String googleCredentialsFile = null; 6 | private String googleCredentials = null; 7 | private long defaultCheckInWaitTimeMs = 10000; 8 | 9 | public boolean isEnabled() { 10 | return enabled; 11 | } 12 | 13 | public void setEnabled(boolean enabled) { 14 | this.enabled = enabled; 15 | } 16 | 17 | public String getGoogleCredentialsFile() { 18 | return googleCredentialsFile; 19 | } 20 | 21 | public void setGoogleCredentialsFile(String googleCredentialsFile) { 22 | this.googleCredentialsFile = googleCredentialsFile; 23 | } 24 | 25 | public String getGoogleCredentials() { 26 | return googleCredentials; 27 | } 28 | 29 | public void setGoogleCredentials(String googleCredentials) { 30 | this.googleCredentials = googleCredentials; 31 | } 32 | 33 | public long getDefaultCheckInWaitTimeMs() { 34 | return defaultCheckInWaitTimeMs; 35 | } 36 | 37 | public void setDefaultCheckInWaitTimeMs(long defaultCheckInWaitTimeMs) { 38 | this.defaultCheckInWaitTimeMs = defaultCheckInWaitTimeMs; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/formItems/formGroups/MapInputFormGroup.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import MapInput from '../MapInput'; 3 | import { FormGroup, ControlLabel } from 'react-bootstrap/lib'; 4 | 5 | const MapInputFormGroup = (props) => ( 6 | 7 | {props.label} 8 | 13 | 14 | ); 15 | 16 | MapInputFormGroup.propTypes = { 17 | id: PropTypes.string.isRequired, 18 | label: PropTypes.string.isRequired, 19 | onChange: PropTypes.func.isRequired, 20 | renderKeyField: PropTypes.func, // Function of signature (currentValue, onChange) => Node 21 | renderValueField: PropTypes.func, // Function of signature (currentValue, onChange) => Node 22 | keyPlaceholder: PropTypes.string, 23 | valuePlaceholder: PropTypes.string, 24 | keyHeader: PropTypes.string.isRequired, 25 | valueHeader: PropTypes.string.isRequired, 26 | value: PropTypes.arrayOf(React.PropTypes.shape({ 27 | key: PropTypes.any, 28 | value: PropTypes.any 29 | })).isRequired, 30 | required: PropTypes.bool 31 | }; 32 | 33 | export default MapInputFormGroup; 34 | -------------------------------------------------------------------------------- /BaragonUI/app/components/serviceDetail/DetailHeader.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | import ButtonContainer from './ButtonContainer'; 4 | 5 | const DetailHeader = ({id, basePath, editable, serviceJson, upstreams, 6 | afterRemoveUpstreams, afterReload, afterDelete, afterPurgeCache}) => { 7 | return ( 8 |
    9 |
    10 |

    { id }

    11 |
    12 |
    13 |

    { basePath }

    14 |
    15 | 24 |
    25 | ); 26 | }; 27 | 28 | DetailHeader.propTypes = { 29 | id: PropTypes.string, 30 | basePath: PropTypes.string, 31 | editable: PropTypes.bool, 32 | serviceJson: PropTypes.object, 33 | upstreams: PropTypes.array, 34 | afterRemoveUpstreams: PropTypes.func, 35 | afterReload: PropTypes.func, 36 | afterDelete: PropTypes.func, 37 | afterPurgeCache: PropTypes.func, 38 | }; 39 | 40 | export default DetailHeader; 41 | -------------------------------------------------------------------------------- /BaragonUI/app/components/status/PendingRequests.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router'; 3 | 4 | const pendingRequest = ({requestId, appRoot}) => { 5 | return ( 6 |
  • 7 | {requestId} 8 |
  • 9 | ); 10 | }; 11 | 12 | pendingRequest.propTypes = { 13 | requestId: React.PropTypes.string, 14 | appRoot: React.PropTypes.string, 15 | }; 16 | 17 | const requestsBox = (queuedRequests) => { 18 | if (queuedRequests && queuedRequests.length) { 19 | return ( 20 |
      21 | { queuedRequests.map(pendingRequest) } 22 |
    23 | ); 24 | } else { 25 | return

    No pending requests

    ; 26 | } 27 | }; 28 | 29 | const PendingRequests = ({queuedRequests}) => { 30 | return ( 31 |
    32 |

    Pending Requests

    33 | {requestsBox(queuedRequests)} 34 |
    35 | ); 36 | }; 37 | 38 | PendingRequests.propTypes = { 39 | queuedRequests: React.PropTypes.arrayOf(React.PropTypes.shape({ 40 | requestId: React.PropTypes.string, 41 | appRoot: React.PropTypes.string, 42 | })) 43 | }; 44 | 45 | export default PendingRequests; 46 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/ReloadServiceButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | import { Glyphicon } from 'react-bootstrap'; 4 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 5 | import ToolTip from 'react-bootstrap/lib/Tooltip'; 6 | 7 | import { getClickComponent } from '../modal/ModalWrapper'; 8 | 9 | import ReloadServiceModal from './ReloadServiceModal'; 10 | 11 | const reloadTooltip = ( 12 | 13 | Reload Service Config 14 | 15 | ); 16 | 17 | export default class ReloadServiceButton extends Component { 18 | static propTypes = { 19 | serviceId: PropTypes.string.isRequired, 20 | children: PropTypes.node, 21 | then: PropTypes.func 22 | }; 23 | 24 | static defaultProps = { 25 | children: ( 26 | 27 | 28 | 29 | 30 | 31 | ) 32 | }; 33 | 34 | render() { 35 | return ( 36 | 37 | {getClickComponent(this)} 38 | 39 | 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /BaragonAgentService/src/main/java/com/hubspot/baragon/agent/resources/BatchRequestResource.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.agent.resources; 2 | 3 | import com.google.inject.Inject; 4 | import com.hubspot.baragon.agent.managers.AgentRequestManager; 5 | import com.hubspot.baragon.models.AgentBatchResponseItem; 6 | import com.hubspot.baragon.models.BaragonRequestBatchItem; 7 | import java.util.List; 8 | import javax.ws.rs.Consumes; 9 | import javax.ws.rs.POST; 10 | import javax.ws.rs.Path; 11 | import javax.ws.rs.Produces; 12 | import javax.ws.rs.core.MediaType; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | @Path("/batch") 17 | @Produces(MediaType.APPLICATION_JSON) 18 | public class BatchRequestResource { 19 | private static final Logger LOG = LoggerFactory.getLogger(BatchRequestResource.class); 20 | private final AgentRequestManager agentRequestManager; 21 | 22 | @Inject 23 | public BatchRequestResource(AgentRequestManager agentRequestManager) { 24 | this.agentRequestManager = agentRequestManager; 25 | } 26 | 27 | @POST 28 | @Consumes(MediaType.APPLICATION_JSON) 29 | public List apply(List batch) 30 | throws InterruptedException { 31 | return agentRequestManager.processRequests(batch); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /BaragonUI/app/actions/api/services.es6: -------------------------------------------------------------------------------- 1 | import { buildApiAction, buildJsonApiAction } from './base'; 2 | 3 | export const FetchBaragonServices = buildApiAction( 4 | 'FETCH_BARAGON_SERVICES', 5 | {url: '/state'} 6 | ); 7 | 8 | export const FetchService = buildApiAction( 9 | 'FETCH_SERVICE', 10 | (serviceId, renderNotFoundIf404) => ({ 11 | url: `/state/${serviceId}`, 12 | renderNotFoundIf404 13 | }), 14 | (serviceId) => serviceId 15 | ); 16 | 17 | export const FetchRenderedConfigs = buildApiAction( 18 | 'FETCH_RENDERED_CONFIGS', 19 | (serviceId) => ({ 20 | url: `/renderedConfigs/${serviceId}` 21 | }), 22 | (serviceId) => serviceId 23 | ); 24 | 25 | export const DeleteService = buildJsonApiAction( 26 | 'DELETE_SERVICE', 27 | 'DELETE', 28 | (serviceId, noValidate = false, noReload = false) => ({ 29 | url: `/state/${serviceId}?noValidate=${noValidate}&noReload=${noReload}`, 30 | }) 31 | ); 32 | 33 | export const PurgeCache = buildJsonApiAction( 34 | 'PURGE_CACHE', 35 | 'POST', 36 | (serviceId) => ({ 37 | url: `/purgeCache/${serviceId}`, 38 | }) 39 | ); 40 | 41 | export const ReloadService = buildJsonApiAction( 42 | 'RELOAD_SERVICE', 43 | 'POST', 44 | (serviceId) => ({ 45 | url: `/state/${serviceId}/reload`, 46 | }) 47 | ); 48 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/AddTrafficSourceButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 4 | import ToolTip from 'react-bootstrap/lib/Tooltip'; 5 | 6 | import { getClickComponent } from '../modal/ModalWrapper'; 7 | 8 | import AddTrafficSourceModal from './AddTrafficSourceModal'; 9 | 10 | const AddTrafficSourceTooltip = ( 11 | 12 | Add Traffic Source 13 | 14 | ); 15 | 16 | export default class AddTrafficSourceButton extends Component { 17 | static propTypes = { 18 | groupName: PropTypes.string.isRequired, 19 | children: PropTypes.node, 20 | then: PropTypes.func 21 | }; 22 | 23 | static defaultProps = { 24 | children: ( 25 | 26 | 27 | + Add Traffic Source 28 | 29 | 30 | ) 31 | }; 32 | 33 | render() { 34 | return ( 35 | 36 | {getClickComponent(this)} 37 | 38 | 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/DeleteServiceButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | import { Glyphicon } from 'react-bootstrap'; 4 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 5 | import ToolTip from 'react-bootstrap/lib/Tooltip'; 6 | 7 | import { getClickComponent } from '../modal/ModalWrapper'; 8 | 9 | import DeleteServiceModal from './DeleteServiceModal'; 10 | 11 | const deleteTooltip = ( 12 | 13 | Delete Service Config 14 | 15 | ); 16 | 17 | export default class DeleteServiceButton extends Component { 18 | static propTypes = { 19 | serviceId: PropTypes.string.isRequired, 20 | children: PropTypes.node, 21 | then: PropTypes.func 22 | }; 23 | 24 | static defaultProps = { 25 | children: ( 26 | 27 | 28 | 29 | 30 | 31 | ) 32 | }; 33 | 34 | render() { 35 | return ( 36 | 37 | {getClickComponent(this)} 38 | 39 | 40 | ); 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.0' 2 | services: 3 | zk: 4 | image: bobrik/zookeeper 5 | network_mode: host 6 | environment: 7 | ZK_CONFIG: tickTime=2000,initLimit=10,syncLimit=5,maxClientCnxns=128,forceSync=no,clientPort=2181 8 | ZK_ID: 1 9 | expose: 10 | - 2181 11 | service: 12 | image: hubspot/baragonservice:0.4.0 13 | network_mode: host 14 | environment: 15 | DOCKER_HOST: 16 | BARAGON_PORT: 8080 17 | BARAGON_UI_BASE: /baragon/v2 18 | agent1: 19 | image: hubspot/baragonagent:0.6.2 20 | network_mode: host 21 | environment: 22 | NGINX_PORT: 80 23 | BARAGON_PORT: 8882 24 | BARAGON_AGENT_GROUP: test 25 | volumes: 26 | - ./docker/configs/1/custom:/etc/nginx/conf.d/custom 27 | - ./docker/configs/1/proxy:/etc/nginx/conf.d/proxy 28 | - ./docker/configs/1/upstreams:/etc/nginx/conf.d/upstreams 29 | agent2: 30 | image: hubspot/baragonagent:0.6.2 31 | network_mode: host 32 | environment: 33 | NGINX_PORT: 81 34 | BARAGON_PORT: 8883 35 | BARAGON_AGENT_GROUP: test 36 | volumes: 37 | - ./docker/configs/2/custom:/etc/nginx/conf.d/custom 38 | - ./docker/configs/2/proxy:/etc/nginx/conf.d/proxy 39 | - ./docker/configs/2/upstreams:/etc/nginx/conf.d/upstreams 40 | -------------------------------------------------------------------------------- /BaragonAgentService/src/main/java/com/hubspot/baragon/agent/workers/AgentHeartbeatWorker.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.agent.workers; 2 | 3 | import com.google.inject.Inject; 4 | import com.hubspot.baragon.agent.config.LoadBalancerConfiguration; 5 | import com.hubspot.baragon.data.BaragonKnownAgentsDatastore; 6 | import com.hubspot.baragon.models.BaragonAgentMetadata; 7 | 8 | public class AgentHeartbeatWorker implements Runnable { 9 | private final BaragonKnownAgentsDatastore knownAgentsDatastore; 10 | private final BaragonAgentMetadata baragonAgentMetadata; 11 | private final LoadBalancerConfiguration loadBalancerConfiguration; 12 | 13 | @Inject 14 | public AgentHeartbeatWorker( 15 | BaragonKnownAgentsDatastore knownAgentsDatastore, 16 | BaragonAgentMetadata baragonAgentMetadata, 17 | LoadBalancerConfiguration loadBalancerConfiguration 18 | ) { 19 | this.knownAgentsDatastore = knownAgentsDatastore; 20 | this.baragonAgentMetadata = baragonAgentMetadata; 21 | this.loadBalancerConfiguration = loadBalancerConfiguration; 22 | } 23 | 24 | @Override 25 | public void run() { 26 | knownAgentsDatastore.updateKnownAgentLastSeenAt( 27 | loadBalancerConfiguration.getName(), 28 | baragonAgentMetadata.getAgentId(), 29 | System.currentTimeMillis() 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /BaragonAgentService/src/main/java/com/hubspot/baragon/agent/resources/RenderedConfigsResource.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.agent.resources; 2 | 3 | import com.google.inject.Inject; 4 | import com.hubspot.baragon.agent.managers.AgentRequestManager; 5 | import com.hubspot.baragon.auth.NoAuth; 6 | import javax.ws.rs.GET; 7 | import javax.ws.rs.Path; 8 | import javax.ws.rs.PathParam; 9 | import javax.ws.rs.Produces; 10 | import javax.ws.rs.core.MediaType; 11 | import javax.ws.rs.core.Response; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | @Path("/renderedConfigs") 16 | @Produces(MediaType.APPLICATION_JSON) 17 | public class RenderedConfigsResource { 18 | private static final Logger LOG = LoggerFactory.getLogger( 19 | RenderedConfigsResource.class 20 | ); 21 | 22 | private final AgentRequestManager agentRequestManager; 23 | 24 | @Inject 25 | public RenderedConfigsResource(AgentRequestManager agentRequestManager) { 26 | this.agentRequestManager = agentRequestManager; 27 | } 28 | 29 | @GET 30 | @NoAuth 31 | @Path("/{serviceId}") 32 | public Response getServiceId(@PathParam("serviceId") String serviceId) { 33 | LOG.info("Received request to view the renderedConfig of serviceId={}", serviceId); 34 | return agentRequestManager.getRenderedConfigs(serviceId); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /compose-dev.yml: -------------------------------------------------------------------------------- 1 | version: '2.0' 2 | services: 3 | zk: 4 | image: bobrik/zookeeper 5 | network_mode: host 6 | environment: 7 | ZK_CONFIG: tickTime=2000,initLimit=10,syncLimit=5,maxClientCnxns=128,forceSync=no,clientPort=2181 8 | ZK_ID: 1 9 | expose: 10 | - 2181 11 | service: 12 | image: hubspot/baragonservice:0.10.0-SNAPSHOT 13 | network_mode: host 14 | environment: 15 | DOCKER_HOST: 16 | BARAGON_PORT: 8080 17 | BARAGON_UI_BASE: /baragon/v2 18 | agent1: 19 | image: hubspot/baragonagent:0.10.0-SNAPSHOT 20 | network_mode: host 21 | environment: 22 | NGINX_PORT: 80 23 | BARAGON_PORT: 8882 24 | BARAGON_AGENT_GROUP: test 25 | volumes: 26 | - ./docker/configs/1/custom:/etc/nginx/conf.d/custom 27 | - ./docker/configs/1/proxy:/etc/nginx/conf.d/proxy 28 | - ./docker/configs/1/upstreams:/etc/nginx/conf.d/upstreams 29 | agent2: 30 | image: hubspot/baragonagent:0.10.0-SNAPSHOT 31 | network_mode: host 32 | environment: 33 | NGINX_PORT: 81 34 | BARAGON_PORT: 8883 35 | BARAGON_AGENT_GROUP: test 36 | volumes: 37 | - ./docker/configs/2/custom:/etc/nginx/conf.d/custom 38 | - ./docker/configs/2/proxy:/etc/nginx/conf.d/proxy 39 | - ./docker/configs/2/upstreams:/etc/nginx/conf.d/upstreams 40 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/ReloadServiceModal.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import { ReloadService } from '../../../actions/api/services'; 5 | 6 | import FormModal from '../modal/FormModal'; 7 | 8 | class ReloadServiceModal extends Component { 9 | static propTypes = { 10 | serviceId: PropTypes.string.isRequired, 11 | reloadService: PropTypes.func.isRequired 12 | }; 13 | 14 | show() { 15 | this.refs.reloadServiceModal.show(); 16 | } 17 | 18 | render() { 19 | return ( 20 | 27 |

    Are you sure you want to reload configs this service?

    28 |
    {this.props.serviceId}
    29 |
    30 | ); 31 | } 32 | } 33 | 34 | const mapDispatchToProps = (dispatch, ownProps) => ({ 35 | reloadService: () => dispatch(ReloadService.trigger(ownProps.serviceId)).then(response => (ownProps.then && ownProps.then(response))) 36 | }); 37 | 38 | export default connect( 39 | null, 40 | mapDispatchToProps, 41 | null, 42 | { withRef: true } 43 | )(ReloadServiceModal); -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/globalSearch.styl: -------------------------------------------------------------------------------- 1 | .global-search-active.global-search 2 | display block 3 | 4 | .global-search 5 | position fixed 6 | z-index 8000 7 | top 0 8 | left 0 9 | bottom 0 10 | right 0 11 | background rgba(255, 255, 255, .98) 12 | 13 | .close-button-container 14 | margin 20px 0 50px 15 | text-align right 16 | 17 | a 18 | color inherit 19 | font-size 50px 20 | 21 | &:hover 22 | text-decoration none 23 | 24 | .tip 25 | text-align right 26 | 27 | strong 28 | font-weight 400 29 | color rgba(0, 0, 0, .25) 30 | 31 | input[type="search"] 32 | background rgba(240, 240, 240, .9) 33 | 34 | &, &::-webkit-search-cancel-button:after 35 | font-size 20px !important 36 | 37 | // Better typeahead styles 38 | .typeahead 39 | ul 40 | list-style-type: none 41 | padding: 0 42 | margin: 0 43 | 44 | &, .typeahead-selector, li 45 | display block !important 46 | width 100% 47 | 48 | li 49 | padding: 10px 50 | cursor: pointer 51 | 52 | p 53 | margin-bottom: 0 54 | 55 | &.hover 56 | background rgba(0, 0, 0, .03) 57 | -------------------------------------------------------------------------------- /BaragonUI/app/components/groupDetail/GroupTitleBar.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | import ModifyCountButton from '../common/modalButtons/ModifyCountButton'; 4 | 5 | const modifyTargetCount = (editable, groupName, currentCount, afterModifyTargetCount) => { 6 | if (editable) { 7 | return ( 8 | 13 | ); 14 | } else { 15 | return null; 16 | } 17 | }; 18 | 19 | const GroupTitleBar = ({group, domain, targetCount, editable, afterModifyTargetCount}) => { 20 | return ( 21 |
    22 |
    23 |

    Group: {group}

    24 |
    25 |
    26 |

    Default Domain: {domain}

    27 |
    28 |
    29 |

    Target Count: {targetCount} {modifyTargetCount(editable, group, targetCount, afterModifyTargetCount)}

    30 |
    31 |
    32 | ); 33 | }; 34 | 35 | GroupTitleBar.propTypes = { 36 | group: PropTypes.string, 37 | domain: PropTypes.string, 38 | targetCount: PropTypes.number, 39 | editable: PropTypes.bool, 40 | afterModifyTargetCount: PropTypes.func, 41 | }; 42 | 43 | export default GroupTitleBar; 44 | -------------------------------------------------------------------------------- /BaragonUI/app/components/elbDetail/HealthCheckPanel.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | const ListItem = ({name, value}) => { 4 | if (value) { 5 | return
  • {name} {value}
  • ; 6 | } else { 7 | return null; 8 | } 9 | }; 10 | 11 | ListItem.propTypes = { 12 | name: PropTypes.string, 13 | value: PropTypes.number, 14 | }; 15 | 16 | const HealthCheckPanel = ({healthCheck: {target, interval, timeout, unhealthyThreshold, healthyThreshold}}) => { 17 | return ( 18 |
    19 |
      20 |
    • Health Check: {target}
    • 21 | 22 | 23 | 24 | 25 |
    26 |
    27 | ); 28 | }; 29 | 30 | HealthCheckPanel.propTypes = { 31 | healthCheck: PropTypes.shape({ 32 | target: PropTypes.string, 33 | interval: PropTypes.number, 34 | timeout: PropTypes.number, 35 | unhealthyThreshold: PropTypes.number, 36 | healthyThreshold: PropTypes.number, 37 | }).isRequired, 38 | }; 39 | 40 | export default HealthCheckPanel; 41 | -------------------------------------------------------------------------------- /Docs/managing/terminology.md: -------------------------------------------------------------------------------- 1 | ## Terminology 2 | - `BaragonService` - The api and accompanying worker that is central to all agents. BaragonService manages all requests, provides information about running services, and also serves up the `BaragonUI` 3 | - `BaragonAgent` or `BaragonAgentService` - The process running on each load balancer host. The agent is responsible for actually applying requests as resulting configuration files using templates 4 | - `BaragonUI` - A backbone.js user interface with the same capabilities as most `BaragonService` endpoints 5 | - `cluster` - All `BaragonService` and `BaragonAgent` instances in the same zookeeper quorum and namespace 6 | - `loadBalancerService` - An ID unique across a cluster which is the primary key for storing all information (paths, upstreams, etc) once a request has been successfuly applied. Associated with the `BaragonService`/`BaragonServiceState` objects 7 | - `loadBalancerRequest` - A `BaragonRequest` object that can be posted to `BaragonService` to either update or create a `loadBalancerService` 8 | - `loadBalancerGroup` - A group of agents that should all have identical configuration, grouped by a name. For example, several nginx load balancers behind the same ELB, or serving content for the same domain 9 | - `basePath` - The subpath on a loadbalancer at which the applied service should be available, must be unique within a `loadBalancerGroup` -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/definitionList.styl: -------------------------------------------------------------------------------- 1 | @import nib 2 | 3 | // Okay so it's not REALLY a definition list, but hey 4 | ul.horizontal-description-list 5 | 6 | li 7 | position relative 8 | overflow hidden 9 | height 69px 10 | margin-bottom 20px 11 | 12 | &, div 13 | border-radius 4px 14 | 15 | div 16 | position relative 17 | width 100% 18 | height 69px 19 | 20 | h4 21 | margin-top 0 22 | 23 | &:hover 24 | overflow visible 25 | z-index 500 26 | 27 | div 28 | height auto 29 | box-shadow 0 0 0 5px rgba(255, 255, 255, .9) 30 | 31 | p 32 | height auto 33 | overflow visible 34 | word-break break-all 35 | white-space pre-wrap 36 | 37 | div 38 | background #f3f3f3 39 | padding 10px 40 | 41 | h4 a 42 | float right 43 | font-weight 400 44 | font-size 12px 45 | 46 | &:hover 47 | text-decoration none 48 | 49 | p 50 | margin-bottom 0 51 | height 1.3em 52 | white-space nowrap 53 | text-overflow ellipsis 54 | overflow hidden 55 | -------------------------------------------------------------------------------- /BaragonData/src/main/java/com/hubspot/baragon/migrations/ZkDataMigration.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.migrations; 2 | 3 | import java.util.Objects; 4 | 5 | public abstract class ZkDataMigration implements Comparable { 6 | 7 | public abstract void applyMigration(); 8 | 9 | private final int migrationNumber; 10 | 11 | public ZkDataMigration(int migrationNumber) { 12 | this.migrationNumber = migrationNumber; 13 | } 14 | 15 | @Override 16 | public String toString() { 17 | return String.format("%s - %s", migrationNumber, getClass().getSimpleName()); 18 | } 19 | 20 | public int getMigrationNumber() { 21 | return migrationNumber; 22 | } 23 | 24 | @Override 25 | public int hashCode() { 26 | return Objects.hashCode(migrationNumber); 27 | } 28 | 29 | @Override 30 | public boolean equals(Object obj) { 31 | if (this == obj) { 32 | return true; 33 | } 34 | if (obj == null) { 35 | return false; 36 | } 37 | if (getClass() != obj.getClass()) { 38 | return false; 39 | } 40 | ZkDataMigration other = (ZkDataMigration) obj; 41 | if (migrationNumber != other.migrationNumber) { 42 | return false; 43 | } 44 | return true; 45 | } 46 | 47 | @Override 48 | public int compareTo(ZkDataMigration o) { 49 | return Integer.compare(migrationNumber, o.migrationNumber); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /BaragonData/src/main/java/com/hubspot/baragon/cache/CachedBaragonState.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.cache; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.OutputStream; 7 | import java.util.zip.GZIPOutputStream; 8 | 9 | public class CachedBaragonState { 10 | private final byte[] uncompressed; 11 | private final byte[] gzip; 12 | private final int version; 13 | 14 | @SuppressFBWarnings("EI_EXPOSE_REP2") 15 | public CachedBaragonState(byte[] uncompressed, int version) { 16 | this.uncompressed = uncompressed; 17 | this.gzip = compress(uncompressed); 18 | this.version = version; 19 | } 20 | 21 | @SuppressFBWarnings("EI_EXPOSE_REP") 22 | public byte[] getUncompressed() { 23 | return uncompressed; 24 | } 25 | 26 | @SuppressFBWarnings("EI_EXPOSE_REP") 27 | public byte[] getGzip() { 28 | return gzip; 29 | } 30 | 31 | public int getVersion() { 32 | return version; 33 | } 34 | 35 | private static byte[] compress(byte[] uncompressed) { 36 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 37 | 38 | try (OutputStream gzip = new GZIPOutputStream(baos)) { 39 | gzip.write(uncompressed); 40 | } catch (IOException e) { 41 | throw new RuntimeException(e); 42 | } 43 | 44 | return baos.toByteArray(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/managed/BaragonExceptionNotifierManaged.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.managed; 2 | 3 | import com.google.inject.Inject; 4 | import com.hubspot.baragon.service.exceptions.BaragonExceptionNotifier; 5 | import com.hubspot.baragon.service.exceptions.NotifyingExceptionMapper; 6 | import com.hubspot.baragon.service.exceptions.NotifyingUncaughtExceptionManager; 7 | import io.dropwizard.lifecycle.Managed; 8 | import javax.inject.Singleton; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | @Singleton 13 | public class BaragonExceptionNotifierManaged implements Managed { 14 | private static final Logger LOG = LoggerFactory.getLogger( 15 | NotifyingExceptionMapper.class 16 | ); 17 | 18 | private final BaragonExceptionNotifier exceptionNotifier; 19 | 20 | @Inject 21 | public BaragonExceptionNotifierManaged(BaragonExceptionNotifier exceptionNotifier) { 22 | this.exceptionNotifier = exceptionNotifier; 23 | } 24 | 25 | @Override 26 | public void start() throws Exception { 27 | LOG.info( 28 | "Setting NotifyingUncaughtExceptionManager as the default uncaught exception provider..." 29 | ); 30 | Thread.setDefaultUncaughtExceptionHandler( 31 | new NotifyingUncaughtExceptionManager(exceptionNotifier) 32 | ); 33 | } 34 | 35 | @Override 36 | public void stop() throws Exception {} 37 | } 38 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/PurgeCacheButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | import { Glyphicon } from 'react-bootstrap'; 4 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 5 | import ToolTip from 'react-bootstrap/lib/Tooltip'; 6 | 7 | import { getClickComponent } from '../modal/ModalWrapper'; 8 | 9 | import PurgeCacheModal from "./PurgeCacheModal"; 10 | 11 | const purgeCacheTooltip = ( 12 | 13 | PurgeCache 14 | 15 | ); 16 | 17 | export default class PurgeCacheButton extends Component { 18 | static propTypes = { 19 | serviceId: PropTypes.string.isRequired, 20 | children: PropTypes.node, 21 | then: PropTypes.func 22 | }; 23 | 24 | static defaultProps = { 25 | children: ( 26 | 27 | 28 | 29 | 30 | 31 | ) 32 | }; 33 | 34 | render() { 35 | return ( 36 | 37 | {getClickComponent(this)} 38 | 39 | 40 | ); 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/PurgeCacheModal.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import { PurgeCache } from '../../../actions/api/services'; 5 | 6 | import FormModal from '../modal/FormModal'; 7 | 8 | class PurgeCacheModal extends Component { 9 | static propTypes = { 10 | serviceId: PropTypes.string.isRequired, 11 | purgeCache: PropTypes.func.isRequired 12 | }; 13 | 14 | show() { 15 | this.refs.purgeCacheModal.show(); 16 | } 17 | 18 | render() { 19 | return ( 20 | 27 |

    Are you sure you want to purge this service's cache?

    28 |
    {this.props.serviceId}
    29 |
    30 | ); 31 | } 32 | } 33 | 34 | const mapDispatchToProps = (dispatch, ownProps) => ({ 35 | purgeCache: () => dispatch(PurgeCache.trigger(ownProps.serviceId)).then(response => (ownProps.then && ownProps.then(response))) 36 | }); 37 | 38 | export default connect( 39 | null, 40 | mapDispatchToProps, 41 | null, 42 | { withRef: true } 43 | )(PurgeCacheModal); -------------------------------------------------------------------------------- /BaragonAgentService/src/main/java/com/hubspot/baragon/agent/healthcheck/ZooKeeperHealthcheck.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.agent.healthcheck; 2 | 3 | import com.codahale.metrics.health.HealthCheck; 4 | import com.google.inject.Inject; 5 | import com.google.inject.Singleton; 6 | import com.google.inject.name.Named; 7 | import com.hubspot.baragon.BaragonDataModule; 8 | import java.util.concurrent.atomic.AtomicReference; 9 | import org.apache.curator.framework.state.ConnectionState; 10 | 11 | @Singleton 12 | public class ZooKeeperHealthcheck extends HealthCheck { 13 | private final AtomicReference connectionState; 14 | 15 | @Inject 16 | public ZooKeeperHealthcheck( 17 | @Named( 18 | BaragonDataModule.BARAGON_ZK_CONNECTION_STATE 19 | ) AtomicReference connectionState 20 | ) { 21 | this.connectionState = connectionState; 22 | } 23 | 24 | @Override 25 | protected Result check() throws Exception { 26 | final ConnectionState currentConnectionState = connectionState.get(); 27 | 28 | if (currentConnectionState == null) { 29 | return Result.unhealthy("Connection state is null"); 30 | } 31 | 32 | switch (currentConnectionState) { 33 | case CONNECTED: 34 | case RECONNECTED: 35 | return Result.healthy(currentConnectionState.name()); 36 | default: 37 | return Result.unhealthy(currentConnectionState.name()); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/healthcheck/ZooKeeperHealthcheck.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.healthcheck; 2 | 3 | import com.codahale.metrics.health.HealthCheck; 4 | import com.google.inject.Inject; 5 | import com.google.inject.Singleton; 6 | import com.google.inject.name.Named; 7 | import com.hubspot.baragon.BaragonDataModule; 8 | import java.util.concurrent.atomic.AtomicReference; 9 | import org.apache.curator.framework.state.ConnectionState; 10 | 11 | @Singleton 12 | public class ZooKeeperHealthcheck extends HealthCheck { 13 | private final AtomicReference connectionState; 14 | 15 | @Inject 16 | public ZooKeeperHealthcheck( 17 | @Named( 18 | BaragonDataModule.BARAGON_ZK_CONNECTION_STATE 19 | ) AtomicReference connectionState 20 | ) { 21 | this.connectionState = connectionState; 22 | } 23 | 24 | @Override 25 | protected Result check() throws Exception { 26 | final ConnectionState currentConnectionState = connectionState.get(); 27 | 28 | if (currentConnectionState == null) { 29 | return Result.unhealthy("Connection state is null"); 30 | } 31 | 32 | switch (currentConnectionState) { 33 | case CONNECTED: 34 | case RECONNECTED: 35 | return Result.healthy(currentConnectionState.name()); 36 | default: 37 | return Result.unhealthy(currentConnectionState.name()); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/resources/UIResource.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.resources; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.name.Named; 5 | import com.hubspot.baragon.auth.NoAuth; 6 | import com.hubspot.baragon.service.BaragonServiceModule; 7 | import com.hubspot.baragon.service.config.BaragonConfiguration; 8 | import com.hubspot.baragon.service.views.IndexView; 9 | import javax.inject.Singleton; 10 | import javax.ws.rs.GET; 11 | import javax.ws.rs.Path; 12 | import javax.ws.rs.Produces; 13 | import javax.ws.rs.core.MediaType; 14 | 15 | /** 16 | * Serves as the base for the UI, returns the mustache view for the actual GUI. 17 | */ 18 | @Singleton 19 | @Path(UIResource.UI_RESOURCE_LOCATION + "{uiPath:.*}") 20 | @NoAuth 21 | public class UIResource { 22 | static final String UI_RESOURCE_LOCATION = "/ui"; 23 | 24 | private final BaragonConfiguration configuration; 25 | private final String baragonUriBase; 26 | 27 | @Inject 28 | public UIResource( 29 | @Named(BaragonServiceModule.BARAGON_URI_BASE) String baragonUriBase, 30 | BaragonConfiguration configuration 31 | ) { 32 | this.configuration = configuration; 33 | this.baragonUriBase = baragonUriBase; 34 | } 35 | 36 | @GET 37 | @Produces(MediaType.TEXT_HTML) 38 | public IndexView getIndex() { 39 | return new IndexView(baragonUriBase, UI_RESOURCE_LOCATION, configuration); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/ModifyCountButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | import { Glyphicon } from 'react-bootstrap'; 4 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 5 | import ToolTip from 'react-bootstrap/lib/Tooltip'; 6 | 7 | import { getClickComponent } from '../modal/ModalWrapper'; 8 | 9 | import ModifyCountModal from './ModifyCountModal'; 10 | 11 | const ModifyTargetCountTooltip = ( 12 | 13 | Change Current Target Count 14 | 15 | ); 16 | 17 | export default class ModifyCountButton extends Component { 18 | static propTypes = { 19 | groupName: PropTypes.string.isRequired, 20 | currentCount: PropTypes.number, 21 | children: PropTypes.node, 22 | then: PropTypes.func 23 | }; 24 | 25 | static defaultProps = { 26 | children: ( 27 | 28 | 29 | 30 | 31 | 32 | ) 33 | }; 34 | 35 | render() { 36 | return ( 37 | 38 | {getClickComponent(this)} 39 | 45 | 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /BaragonUI/app/reducers/api/base.es6: -------------------------------------------------------------------------------- 1 | const identity = (a) => a; 2 | 3 | export default function buildApiActionReducer(ActionGroup, initialData = {}, transform = identity) { 4 | const initialState = { 5 | isFetching: false, 6 | error: null, 7 | statusCode: null, 8 | receivedAt: null, 9 | erroredAt: null, 10 | data: initialData 11 | }; 12 | 13 | return function reducer(state = initialState, action) { 14 | let newData = {}; 15 | 16 | switch (action.type) { 17 | 18 | case ActionGroup.CLEAR: 19 | return initialState; 20 | 21 | case ActionGroup.ERROR: 22 | newData = _.extend({}, state, { 23 | isFetching: false, 24 | error: action.error, 25 | statusCode: action.statusCode, 26 | erroredAt: Date.now() 27 | }); 28 | return _.extend({}, state, newData); 29 | 30 | case ActionGroup.SUCCESS: 31 | newData = _.extend({}, state, { 32 | isFetching: false, 33 | error: null, 34 | statusCode: action.statusCode, 35 | receivedAt: Date.now(), 36 | data: transform(action.data) 37 | }); 38 | return _.extend({}, state, newData); 39 | 40 | case ActionGroup.STARTED: 41 | newData = _.extend({}, state, { 42 | isFetching: true 43 | }); 44 | return _.extend({}, state, newData); 45 | 46 | default: 47 | return state; 48 | } 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /BaragonAgentService/src/main/java/com/hubspot/baragon/agent/handlebars/CurrentRackIsPresentHelper.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.agent.handlebars; 2 | 3 | import com.github.jknack.handlebars.Helper; 4 | import com.github.jknack.handlebars.Options; 5 | import com.google.common.base.Optional; 6 | import com.hubspot.baragon.models.UpstreamInfo; 7 | import java.io.IOException; 8 | import java.util.Collection; 9 | 10 | public class CurrentRackIsPresentHelper implements Helper> { 11 | public static final String NAME = "currentRackIsPresent"; 12 | 13 | private final Optional currentRackId; 14 | 15 | public CurrentRackIsPresentHelper(Optional currentRackId) { 16 | this.currentRackId = currentRackId; 17 | } 18 | 19 | @Override 20 | public CharSequence apply(Collection upstreams, Options options) 21 | throws IOException { 22 | if (!currentRackId.isPresent()) { 23 | return options.fn(); 24 | } 25 | 26 | if (upstreams == null) { 27 | return options.inverse(); 28 | } 29 | 30 | for (UpstreamInfo upstreamInfo : upstreams) { 31 | if ( 32 | upstreamInfo.getRackId().isPresent() && 33 | upstreamInfo 34 | .getRackId() 35 | .get() 36 | .toLowerCase() 37 | .equals(currentRackId.get().toLowerCase()) 38 | ) { 39 | return options.fn(); 40 | } 41 | } 42 | return options.inverse(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/RemoveBasePathButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | import { Glyphicon } from 'react-bootstrap'; 4 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 5 | import ToolTip from 'react-bootstrap/lib/Tooltip'; 6 | 7 | import { getClickComponent } from '../modal/ModalWrapper'; 8 | 9 | import RemoveBasePathModal from './RemoveBasePathModal'; 10 | 11 | const RemoveBasePathTooltip = ( 12 | 13 | Remove this base path 14 | 15 | ); 16 | 17 | export default class RemoveBasePathButton extends Component { 18 | static propTypes = { 19 | groupName: PropTypes.string.isRequired, 20 | basePath: PropTypes.string.isRequired, 21 | children: PropTypes.node, 22 | then: PropTypes.func 23 | }; 24 | 25 | static defaultProps = { 26 | children: ( 27 | 28 | 29 | 30 | 31 | 32 | ) 33 | }; 34 | 35 | render() { 36 | return ( 37 | 38 | {getClickComponent(this)} 39 | 45 | 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/RemoveKnownAgentButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | import { Glyphicon } from 'react-bootstrap'; 4 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 5 | import ToolTip from 'react-bootstrap/lib/Tooltip'; 6 | 7 | import { getClickComponent } from '../modal/ModalWrapper'; 8 | 9 | import RemoveKnownAgentModal from './RemoveKnownAgentModal'; 10 | 11 | const removeKnownAgentTooltip = ( 12 | 13 | Remove this agent 14 | 15 | ); 16 | 17 | export default class RemoveKnownAgentButton extends Component { 18 | static propTypes = { 19 | groupName: PropTypes.string.isRequired, 20 | agentId: PropTypes.string.isRequired, 21 | children: PropTypes.node, 22 | then: PropTypes.func 23 | }; 24 | 25 | static defaultProps = { 26 | children: ( 27 | 28 | 29 | 30 | 31 | 32 | ) 33 | }; 34 | 35 | render() { 36 | return ( 37 | 38 | {getClickComponent(this)} 39 | 45 | 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/edgecache/cloudflare/client/models/CloudflareZone.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.edgecache.cloudflare.client.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.google.common.base.Objects; 7 | 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | public class CloudflareZone { 10 | private final String id; 11 | private final String name; 12 | 13 | @JsonCreator 14 | public CloudflareZone( 15 | @JsonProperty("id") String id, 16 | @JsonProperty("name") String name 17 | ) { 18 | this.id = id; 19 | this.name = name; 20 | } 21 | 22 | public String getId() { 23 | return id; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | if (this == o) { 33 | return true; 34 | } 35 | if (o == null || getClass() != o.getClass()) { 36 | return false; 37 | } 38 | CloudflareZone that = (CloudflareZone) o; 39 | return Objects.equal(id, that.id) && Objects.equal(name, that.name); 40 | } 41 | 42 | @Override 43 | public int hashCode() { 44 | return Objects.hashCode(id, name); 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "CloudflareZone{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /BaragonData/src/main/java/com/hubspot/baragon/data/BaragonAliasDatastore.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.data; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.base.Optional; 5 | import com.google.inject.Inject; 6 | import com.hubspot.baragon.config.ZooKeeperConfiguration; 7 | import com.hubspot.baragon.models.BaragonGroupAlias; 8 | import java.util.List; 9 | import org.apache.curator.framework.CuratorFramework; 10 | import org.apache.curator.utils.ZKPaths; 11 | 12 | public class BaragonAliasDatastore extends AbstractDataStore { 13 | public static final String ALIASES_ROOT = "/aliases"; 14 | 15 | @Inject 16 | public BaragonAliasDatastore( 17 | CuratorFramework curatorFramework, 18 | ObjectMapper objectMapper, 19 | ZooKeeperConfiguration zooKeeperConfiguration 20 | ) { 21 | super(curatorFramework, objectMapper, zooKeeperConfiguration); 22 | } 23 | 24 | private String getAliasPath(String name) { 25 | return ZKPaths.makePath(ALIASES_ROOT, name); 26 | } 27 | 28 | public void saveAlias(String name, BaragonGroupAlias alias) { 29 | writeToZk(getAliasPath(name), alias); 30 | } 31 | 32 | public List getAllAliases() { 33 | return getChildren(ALIASES_ROOT); 34 | } 35 | 36 | public Optional getAlias(String name) { 37 | return readFromZk(getAliasPath(name), BaragonGroupAlias.class); 38 | } 39 | 40 | public void deleteAlias(String name) { 41 | deleteNode(getAliasPath(name)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/edgecache/cloudflare/client/models/CloudflareDnsRecord.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.edgecache.cloudflare.client.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.google.common.base.Objects; 6 | 7 | @JsonIgnoreProperties(ignoreUnknown = true) 8 | public class CloudflareDnsRecord { 9 | private final Boolean proxied; 10 | private final String name; 11 | 12 | public CloudflareDnsRecord( 13 | @JsonProperty("proxied") Boolean proxied, 14 | @JsonProperty("name") String name 15 | ) { 16 | this.proxied = proxied; 17 | this.name = name; 18 | } 19 | 20 | public Boolean isProxied() { 21 | return proxied; 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | @Override 29 | public boolean equals(Object o) { 30 | if (this == o) { 31 | return true; 32 | } 33 | if (o == null || getClass() != o.getClass()) { 34 | return false; 35 | } 36 | CloudflareDnsRecord that = (CloudflareDnsRecord) o; 37 | return Objects.equal(proxied, that.proxied) && Objects.equal(name, that.name); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return Objects.hashCode(proxied, name); 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "CloudflareDnsRecord{" + "proxied=" + proxied + ", name='" + name + '\'' + '}'; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/resources/AliasesResource.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.resources; 2 | 3 | import com.google.common.base.Optional; 4 | import com.google.inject.Inject; 5 | import com.hubspot.baragon.data.BaragonAliasDatastore; 6 | import com.hubspot.baragon.models.BaragonGroupAlias; 7 | import javax.ws.rs.Consumes; 8 | import javax.ws.rs.DELETE; 9 | import javax.ws.rs.GET; 10 | import javax.ws.rs.POST; 11 | import javax.ws.rs.Path; 12 | import javax.ws.rs.PathParam; 13 | import javax.ws.rs.Produces; 14 | import javax.ws.rs.core.MediaType; 15 | 16 | @Path("/aliases") 17 | @Produces(MediaType.APPLICATION_JSON) 18 | @Consumes(MediaType.APPLICATION_JSON) 19 | public class AliasesResource { 20 | private final BaragonAliasDatastore aliasDatastore; 21 | 22 | @Inject 23 | public AliasesResource(BaragonAliasDatastore aliasDatastore) { 24 | this.aliasDatastore = aliasDatastore; 25 | } 26 | 27 | @POST 28 | @Path("/{name}") 29 | public BaragonGroupAlias createAlias( 30 | @PathParam("name") String name, 31 | BaragonGroupAlias alias 32 | ) { 33 | aliasDatastore.saveAlias(name, alias); 34 | return alias; 35 | } 36 | 37 | @GET 38 | @Path("/{name}") 39 | public Optional getAlias(@PathParam("name") String name) { 40 | return aliasDatastore.getAlias(name); 41 | } 42 | 43 | @DELETE 44 | @Path("/{name}") 45 | public void deleteAlias(@PathParam("name") String name) { 46 | aliasDatastore.deleteAlias(name); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /BaragonAgentService/src/main/java/com/hubspot/baragon/agent/handlebars/FirstOfHelper.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.agent.handlebars; 2 | 3 | import com.github.jknack.handlebars.Helper; 4 | import com.github.jknack.handlebars.Options; 5 | import com.google.common.base.Optional; 6 | import com.google.common.base.Strings; 7 | import java.io.IOException; 8 | 9 | public class FirstOfHelper implements Helper { 10 | public static final String NAME = "firstOf"; 11 | 12 | private final Object fallback; 13 | 14 | public FirstOfHelper(Object fallback) { 15 | this.fallback = fallback; 16 | } 17 | 18 | public Object getFallback() { 19 | return fallback; 20 | } 21 | 22 | @Override 23 | public CharSequence apply(Object context, Options options) throws IOException { 24 | // handle null 25 | if (context == null) { 26 | return options.param(0, fallback).toString(); 27 | } 28 | 29 | // handle optional 30 | if (context instanceof Optional) { 31 | final Optional contextOptional = (Optional) context; 32 | return contextOptional.or(options.param(0, fallback)).toString(); 33 | } 34 | 35 | // handle empty string 36 | if (context instanceof String) { 37 | final String contextString = (String) context; 38 | 39 | return !Strings.isNullOrEmpty(contextString) 40 | ? contextString 41 | : options.param(0, fallback).toString(); 42 | } 43 | 44 | // otherwise just return context 45 | return context.toString(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /intro.md: -------------------------------------------------------------------------------- 1 | # Baragon [![Build Status](https://travis-ci.org/HubSpot/Baragon.svg?branch=master)](https://travis-ci.org/HubSpot/Baragon) 2 | 3 | ![Baragon](http://i.imgur.com/mCbkbcZ.jpg) 4 | 5 | Baragon is a system for automating load balancer configuration updates. It pairs well with the [Singularity](https://github.com/HubSpot/Singularity) Mesos framework. 6 | 7 | ## Contents 8 | 9 | - About Baragon 10 | - [Basics](Docs/about/basics.md) 11 | - [Getting Started](Docs/about/getting_started.md) 12 | - [Baragon API Docs](Docs/api.md) 13 | - [Managing Baragon](Docs/managing_baragon.md) 14 | - [Terminology](Docs/managing/terminology.md) 15 | - [Launching a Cluster](Docs/managing/launch.md) 16 | - [Baragon Service Setup and Configuration](Docs/managing/launch.md#servicesetup) 17 | - [Baragon Agent Setup and Configuration](Docs/managing/launch.md#agentsetup) 18 | - [Setup With Nginx](Docs/managing/launch.md#nginx) 19 | - [Setup With Haproxy](Docs/managing/launch.md#haproxy) 20 | - [Integration with Singularity](Docs/managing/launch.md#singularity) 21 | - [Data Storage in Zookeeper](Docs/managing/launch.md#zookeeper) 22 | - [Making Requests to Baragon Service](Docs/managing/requests.md) 23 | - [Statuses and Request Flow](Docs/managing/requests.md) 24 | - [Common Exceptions](Docs/managing/requests.md) 25 | - [BasePath Locking and Updating](Docs/managing/basepaths.md) 26 | - [ELB Sync](Docs/managing/elb_sync.md) 27 | - [Developing](Docs/development.md) 28 | - [Example Configuration](Docs/config/config.md) -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/edgecache/cloudflare/client/models/CloudflarePurgeRequest.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.edgecache.cloudflare.client.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 5 | import com.google.common.base.Objects; 6 | import java.util.List; 7 | 8 | @JsonInclude(Include.NON_EMPTY) 9 | public class CloudflarePurgeRequest { 10 | private final List files; 11 | private final List tags; 12 | 13 | public CloudflarePurgeRequest(List files, List tags) { 14 | this.files = files; 15 | this.tags = tags; 16 | } 17 | 18 | public List getFiles() { 19 | return files; 20 | } 21 | 22 | public List getTags() { 23 | return tags; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object o) { 28 | if (this == o) { 29 | return true; 30 | } 31 | 32 | if (o == null || getClass() != o.getClass()) { 33 | return false; 34 | } 35 | 36 | CloudflarePurgeRequest that = (CloudflarePurgeRequest) o; 37 | return ( 38 | java.util.Objects.equals(files, that.files) && 39 | java.util.Objects.equals(tags, that.tags) 40 | ); 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | return Objects.hashCode(files, tags); 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "CloudflarePurgeRequest{" + "files=" + files + ", tags=" + tags + '}'; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/edgecache/cloudflare/client/models/CloudflareError.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.edgecache.cloudflare.client.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.google.common.base.Objects; 7 | 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | public class CloudflareError { 10 | private final Integer code; 11 | private final String message; 12 | 13 | @JsonCreator 14 | public CloudflareError( 15 | @JsonProperty("code") Integer code, 16 | @JsonProperty("message") String message 17 | ) { 18 | this.code = code; 19 | this.message = message; 20 | } 21 | 22 | public Integer getCode() { 23 | return code; 24 | } 25 | 26 | public String getMessage() { 27 | return message; 28 | } 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | if (this == o) { 33 | return true; 34 | } 35 | if (o == null || getClass() != o.getClass()) { 36 | return false; 37 | } 38 | CloudflareError that = (CloudflareError) o; 39 | return Objects.equal(code, that.code) && Objects.equal(message, that.message); 40 | } 41 | 42 | @Override 43 | public int hashCode() { 44 | return Objects.hashCode(code, message); 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "CloudflareError{" + "code=" + code + ", message='" + message + '\'' + '}'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/formItems/formGroups/TextFormGroup.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import { FormGroup, ControlLabel, FormControl, InputGroup } from 'react-bootstrap/lib'; 3 | 4 | const TextFormGroup = (props) => { 5 | const formField = ( 6 | props.onChange(event)} 11 | /> 12 | ); 13 | const feedback = props.feedback && ; 14 | return ( 15 | 16 | {props.label} 17 | {props.inputGroupAddon && 18 | 19 | {formField} 20 | {props.inputGroupAddon} 21 | 22 | } 23 | {!props.inputGroupAddon && formField} 24 | {!props.inputGroupAddon && feedback} 25 | 26 | ); 27 | }; 28 | 29 | TextFormGroup.propTypes = { 30 | id: PropTypes.string.isRequired, 31 | label: PropTypes.string.isRequired, 32 | onChange: PropTypes.func.isRequired, 33 | placeholder: PropTypes.string, 34 | value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 35 | required: PropTypes.bool, 36 | feedback: PropTypes.oneOf(['SUCCESS', 'ERROR', 'WARN']), 37 | inputGroupAddon: PropTypes.oneOfType([PropTypes.element, PropTypes.string]) 38 | }; 39 | 40 | export default TextFormGroup; 41 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/config/BaragonWorkerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import javax.validation.constraints.Min; 5 | 6 | @JsonIgnoreProperties(ignoreUnknown = true) 7 | public class BaragonWorkerConfiguration { 8 | private boolean enabled = true; 9 | 10 | @Min(1) 11 | private int intervalMs = 1000; 12 | 13 | @Min(0) 14 | private int initialDelayMs = 0; 15 | 16 | @Min(1) 17 | private int maxBatchSize = 50; 18 | 19 | private int maxRequestsPerPoll = 10; 20 | 21 | public boolean isEnabled() { 22 | return enabled; 23 | } 24 | 25 | public void setEnabled(boolean enabled) { 26 | this.enabled = enabled; 27 | } 28 | 29 | public int getIntervalMs() { 30 | return intervalMs; 31 | } 32 | 33 | public void setIntervalMs(int intervalMs) { 34 | this.intervalMs = intervalMs; 35 | } 36 | 37 | public int getInitialDelayMs() { 38 | return initialDelayMs; 39 | } 40 | 41 | public void setInitialDelayMs(int initialDelayMs) { 42 | this.initialDelayMs = initialDelayMs; 43 | } 44 | 45 | public int getMaxBatchSize() { 46 | return maxBatchSize; 47 | } 48 | 49 | public void setMaxBatchSize(int maxBatchSize) { 50 | this.maxBatchSize = maxBatchSize; 51 | } 52 | 53 | public int getMaxRequestsPerPoll() { 54 | return maxRequestsPerPoll; 55 | } 56 | 57 | public void setMaxRequestsPerPoll(int maxRequestsPerPoll) { 58 | this.maxRequestsPerPoll = maxRequestsPerPoll; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modal/FormModalButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import FormModal from './FormModal'; 3 | import {OverlayTrigger, Tooltip} from 'react-bootstrap'; 4 | 5 | const FormModalButton = (props) => { 6 | let modal; 7 | const button = modal.show()} >{props.buttonChildren}; 8 | let buttonWithMaybeTooltip = button; 9 | if (props.tooltipText) { 10 | buttonWithMaybeTooltip = ( 11 | {props.tooltipText}}> 14 | {button} 15 | 16 | ); 17 | } 18 | return ( 19 | 20 | {buttonWithMaybeTooltip} 21 | { modal = modalRef; }} 24 | action={props.action} 25 | onConfirm={(data) => props.onConfirm(data)} 26 | buttonStyle="danger" 27 | formElements={props.formElements}> 28 | {props.children} 29 | 30 | 31 | ); 32 | }; 33 | 34 | FormModalButton.propTypes = { 35 | buttonChildren: PropTypes.node.isRequired, 36 | action: PropTypes.string.isRequired, 37 | onConfirm: PropTypes.func.isRequired, 38 | children: PropTypes.node.isRequired, 39 | tooltipText: PropTypes.string, 40 | name: PropTypes.string, 41 | formElements: PropTypes.arrayOf(PropTypes.shape({ 42 | name: PropTypes.string.isRequired, 43 | type: PropTypes.string.isRequired, 44 | label: PropTypes.string 45 | })) 46 | }; 47 | 48 | export default FormModalButton; 49 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/config/EdgeCacheConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.hubspot.baragon.service.edgecache.cloudflare.EdgeCacheClass; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import javax.validation.constraints.NotNull; 9 | 10 | @JsonIgnoreProperties(ignoreUnknown = true) 11 | public class EdgeCacheConfiguration { 12 | @JsonProperty 13 | @NotNull 14 | private boolean enabled = false; 15 | 16 | @JsonProperty 17 | @NotNull 18 | private EdgeCacheClass edgeCache = EdgeCacheClass.CLOUDFLARE; 19 | 20 | @JsonProperty 21 | @NotNull 22 | private Map integrationSettings = new HashMap<>(); 23 | 24 | public boolean isEnabled() { 25 | return enabled; 26 | } 27 | 28 | public EdgeCacheConfiguration setEnabled(boolean enabled) { 29 | this.enabled = enabled; 30 | return this; 31 | } 32 | 33 | public EdgeCacheClass getEdgeCache() { 34 | return edgeCache; 35 | } 36 | 37 | public EdgeCacheConfiguration setEdgeCache(EdgeCacheClass edgeCache) { 38 | this.edgeCache = edgeCache; 39 | return this; 40 | } 41 | 42 | public Map getIntegrationSettings() { 43 | return integrationSettings; 44 | } 45 | 46 | public EdgeCacheConfiguration setIntegrationSettings( 47 | Map integrationSettings 48 | ) { 49 | this.integrationSettings = integrationSettings; 50 | return this; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /BaragonAgentService/src/main/java/com/hubspot/baragon/agent/config/TestingConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.agent.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public class TestingConfiguration { 6 | @JsonProperty("enabled") 7 | private boolean enabled = false; 8 | 9 | @JsonProperty("applyDelayMs") 10 | private long applyDelayMs = 0; 11 | 12 | @JsonProperty("applyFailRate") 13 | private float applyFailRate = 0; 14 | 15 | @JsonProperty("revertDelayMs") 16 | public long revertDelayMs = 0; 17 | 18 | @JsonProperty("revertFailRate") 19 | public float revertFailRate = 0; 20 | 21 | public boolean isEnabled() { 22 | return enabled; 23 | } 24 | 25 | public void setEnabled(boolean enabled) { 26 | this.enabled = enabled; 27 | } 28 | 29 | public long getApplyDelayMs() { 30 | return applyDelayMs; 31 | } 32 | 33 | public void setApplyDelayMs(long applyDelayMs) { 34 | this.applyDelayMs = applyDelayMs; 35 | } 36 | 37 | public long getRevertDelayMs() { 38 | return revertDelayMs; 39 | } 40 | 41 | public void setRevertDelayMs(long revertDelayMs) { 42 | this.revertDelayMs = revertDelayMs; 43 | } 44 | 45 | public float getApplyFailRate() { 46 | return applyFailRate; 47 | } 48 | 49 | public void setApplyFailRate(float applyFailRate) { 50 | this.applyFailRate = applyFailRate; 51 | } 52 | 53 | public float getRevertFailRate() { 54 | return revertFailRate; 55 | } 56 | 57 | public void setRevertFailRate(float revertFailRate) { 58 | this.revertFailRate = revertFailRate; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /BaragonData/src/main/java/com/hubspot/baragon/cache/BaragonStateCache.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.cache; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import com.hubspot.baragon.data.BaragonStateDatastore; 6 | import java.util.concurrent.atomic.AtomicReference; 7 | 8 | @Singleton 9 | public class BaragonStateCache { 10 | private final BaragonStateDatastore stateDatastore; 11 | private final AtomicReference currentState; 12 | 13 | @Inject 14 | public BaragonStateCache(BaragonStateDatastore stateDatastore) { 15 | this.stateDatastore = stateDatastore; 16 | this.currentState = new AtomicReference<>(new CachedBaragonState(new byte[0], -2)); 17 | } 18 | 19 | public CachedBaragonState getState() { 20 | CachedBaragonState previousState = currentState.get(); 21 | int version = stateDatastore.getStateVersion().or(-1); 22 | 23 | if (previousState.getVersion() == version) { 24 | return previousState; 25 | } else { 26 | return updateState(version); 27 | } 28 | } 29 | 30 | private synchronized CachedBaragonState updateState(int version) { 31 | CachedBaragonState previousState = currentState.get(); 32 | 33 | if (previousState.getVersion() >= version) { 34 | return previousState; 35 | } else { 36 | CachedBaragonState newState = fetchState(version); 37 | currentState.set(newState); 38 | return newState; 39 | } 40 | } 41 | 42 | private CachedBaragonState fetchState(int version) { 43 | return new CachedBaragonState(stateDatastore.getGlobalStateAsBytes(), version); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/EnableEditModal.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import { TestAuthKey } from '../../../actions/api/auth'; 5 | 6 | import FormModal from '../modal/FormModal'; 7 | 8 | class EnableEditModal extends Component { 9 | static propTypes = { 10 | testAuthKey: PropTypes.func.isRequired 11 | }; 12 | 13 | show() { 14 | this.refs.enableEditModal.show(); 15 | } 16 | 17 | render() { 18 | return ( 19 | 32 |

    33 | This Baragon API has auth enabled. You need to specify an 34 | Auth Key in order to initiate any actions, eg test-key. 35 |

    36 |

    37 | This can be changed at any time in the JS console with 38 |

    39 |
    localStorage.setItem("baragonAuthKey", "test-key")
    40 |
    41 | ); 42 | } 43 | } 44 | 45 | const mapDispatchToProps = (dispatch) => ({ 46 | testAuthKey: (data) => (dispatch(TestAuthKey.trigger(data.authKey))), 47 | }); 48 | 49 | export default connect( 50 | null, 51 | mapDispatchToProps, 52 | null, 53 | { withRef: true } 54 | )(EnableEditModal); 55 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/RemoveTrafficSourceButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 4 | import ToolTip from 'react-bootstrap/lib/Tooltip'; 5 | 6 | import { getClickComponent } from '../modal/ModalWrapper'; 7 | 8 | import RemoveTrafficSourceModal from './RemoveTrafficSourceModal'; 9 | 10 | const RemoveTrafficSourceTooltip = ( 11 | 12 | Remove this Traffic Source 13 | 14 | ); 15 | 16 | export default class AddTrafficSourceButton extends Component { 17 | static propTypes = { 18 | groupName: PropTypes.string.isRequired, 19 | trafficSource: PropTypes.shape({ 20 | name: PropTypes.string, 21 | type: PropTypes.string 22 | }).isRequired, 23 | children: PropTypes.node, 24 | then: PropTypes.func 25 | }; 26 | 27 | static defaultProps = { 28 | children: ( 29 | 30 | 31 | 32 | 33 | 34 | ) 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | {getClickComponent(this)} 41 | 47 | 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /BaragonData/src/main/java/com/hubspot/baragon/migrations/ServiceDomainsMigration.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.migrations; 2 | 3 | import com.google.common.base.Optional; 4 | import com.google.inject.Inject; 5 | import com.hubspot.baragon.data.BaragonLoadBalancerDatastore; 6 | import com.hubspot.baragon.data.BaragonStateDatastore; 7 | import com.hubspot.baragon.models.BaragonService; 8 | import java.util.Set; 9 | 10 | public class ServiceDomainsMigration extends ZkDataMigration { 11 | private final BaragonStateDatastore baragonStateDatastore; 12 | private final BaragonLoadBalancerDatastore loadBalancerDatastore; 13 | 14 | @Inject 15 | public ServiceDomainsMigration( 16 | BaragonStateDatastore baragonStateDatastore, 17 | BaragonLoadBalancerDatastore loadBalancerDatastore 18 | ) { 19 | super(2); 20 | this.baragonStateDatastore = baragonStateDatastore; 21 | this.loadBalancerDatastore = loadBalancerDatastore; 22 | } 23 | 24 | @Override 25 | public void applyMigration() { 26 | try { 27 | for (String serviceId : baragonStateDatastore.getServices()) { 28 | Optional service = baragonStateDatastore.getService(serviceId); 29 | if (service.isPresent()) { 30 | Set updatedDomainsWithDefaults = loadBalancerDatastore.getDomainsWithDefaults( 31 | service.get() 32 | ); 33 | baragonStateDatastore.saveService( 34 | service.get().withDomains(updatedDomainsWithDefaults) 35 | ); 36 | } 37 | } 38 | } catch (Throwable t) { 39 | throw new RuntimeException(t); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /BaragonService/src/main/java/com/hubspot/baragon/service/worker/BaragonElbSyncWorker.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.service.worker; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import com.google.inject.name.Named; 6 | import com.hubspot.baragon.BaragonDataModule; 7 | import com.hubspot.baragon.service.exceptions.BaragonExceptionNotifier; 8 | import com.hubspot.baragon.service.managers.ElbManager; 9 | import java.util.concurrent.atomic.AtomicLong; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | @Singleton 14 | public class BaragonElbSyncWorker implements Runnable { 15 | private static final Logger LOG = LoggerFactory.getLogger(BaragonElbSyncWorker.class); 16 | 17 | private final ElbManager elbManager; 18 | private final BaragonExceptionNotifier exceptionNotifier; 19 | private final AtomicLong workerLastStartAt; 20 | 21 | @Inject 22 | public BaragonElbSyncWorker( 23 | ElbManager elbManager, 24 | BaragonExceptionNotifier exceptionNotifier, 25 | @Named(BaragonDataModule.BARAGON_ELB_WORKER_LAST_START) AtomicLong workerLastStartAt 26 | ) { 27 | this.elbManager = elbManager; 28 | this.exceptionNotifier = exceptionNotifier; 29 | this.workerLastStartAt = workerLastStartAt; 30 | } 31 | 32 | @Override 33 | public void run() { 34 | try { 35 | workerLastStartAt.set(System.currentTimeMillis()); 36 | elbManager.syncAll(); 37 | LOG.info("Finished ELB Sync"); 38 | } catch (Exception e) { 39 | LOG.error("Encountered error during ELB sync", e); 40 | exceptionNotifier.notify(e, null); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/EditHealthCheckButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | import { Glyphicon } from 'react-bootstrap'; 4 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 5 | import ToolTip from 'react-bootstrap/lib/Tooltip'; 6 | 7 | import { getClickComponent } from '../modal/ModalWrapper'; 8 | 9 | import EditHealthCheckModal from './EditHealthCheckModal'; 10 | 11 | const editTooltip = ( 12 | 13 | Edit this Target Group's Health Check 14 | 15 | ); 16 | 17 | export default class EditHealthCheckButton extends Component { 18 | static propTypes = { 19 | targetGroupName: PropTypes.string.isRequired, 20 | protocol: PropTypes.oneOf(['HTTP', 'HTTPS']).isRequired, 21 | port: PropTypes.string.isRequired, 22 | path: PropTypes.string.isRequired, 23 | interval: PropTypes.number.isRequired, 24 | timeout: PropTypes.number.isRequired, 25 | healthyThreshold: PropTypes.number.isRequired, 26 | unhealthyThreshold: PropTypes.number.isRequired, 27 | children: PropTypes.node, 28 | then: PropTypes.func 29 | }; 30 | 31 | static defaultProps = { 32 | children: ( 33 | 34 | 35 | 36 | 37 | 38 | ) 39 | }; 40 | 41 | render() { 42 | return ( 43 | 44 | {getClickComponent(this)} 45 | 46 | 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /BaragonUI/app/styles/stylus/loader.styl: -------------------------------------------------------------------------------- 1 | @import colors 2 | @import nib 3 | 4 | .page-loader 5 | height 2em 6 | width 2em 7 | border-radius 100% 8 | border .25em solid transparent 9 | border-top-color $grey 10 | border-right-color $grey 11 | z-index 300 12 | backface-visibility hidden 13 | animation spinner 700ms linear infinite 14 | 15 | &.small 16 | border-width 1px 17 | height 1em 18 | width 1em 19 | 20 | &.fixed 21 | position fixed 22 | margin auto 23 | top 0 24 | left 0 25 | bottom 0 26 | right 0 27 | 28 | &.centered 29 | margin 0 auto 30 | 31 | &.cushy 32 | margin-top 3em 33 | margin-bottom 3em 34 | 35 | &.inline-left 36 | display inline-block 37 | margin-right 5px 38 | 39 | .loader-small 40 | height 1em 41 | width 1em 42 | display inline-block 43 | margin 0 0.5em 44 | 45 | .page-loader-fixed 46 | position: fixed 47 | top: 50% 48 | left: 50% 49 | transform: translate(-50%, -50%) 50 | 51 | .page-loader-with-message 52 | text-align center 53 | margin 2em 0 54 | color #888 55 | 56 | .page-loader 57 | margin-bottom 2em 58 | border-top-color #707070 59 | border-right-color #707070 60 | 61 | p 62 | font-size 1.3em 63 | font-weight 300 64 | letter-spacing .03em 65 | 66 | .empty-table-message + .page-loader-with-message 67 | margin-top 3em 68 | 69 | @keyframes spinner 70 | 0% 71 | transform rotate(0deg) 72 | 100% 73 | transform rotate(360deg) 74 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/RemoveUpstreamButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 4 | import ToolTip from 'react-bootstrap/lib/Tooltip'; 5 | import { Glyphicon } from 'react-bootstrap'; 6 | 7 | import { getClickComponent } from '../modal/ModalWrapper'; 8 | 9 | import RemoveUpstreamModal from './RemoveUpstreamModal'; 10 | 11 | const removeTooltip = ( 12 | 13 | Remove this upstream from the service 14 | 15 | ); 16 | 17 | export default class RemoveUpstreamButton extends Component { 18 | static propTypes = { 19 | loadBalancerService: PropTypes.object, 20 | upstream: PropTypes.shape({ 21 | group: PropTypes.string, 22 | rackId: PropTypes.string, 23 | requestId: PropTypes.string.isRequired, 24 | upstream: PropTypes.string.isRequired, 25 | }), 26 | children: PropTypes.node, 27 | afterRemoveUpstream: PropTypes.func, 28 | }; 29 | 30 | static defaultProps = { 31 | children: ( 32 | 33 | 34 | 35 | ) 36 | }; 37 | 38 | render() { 39 | return ( 40 | 41 | {getClickComponent(this)} 42 | 48 | 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/RemoveBasePathModal.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import { RemoveBasePath } from '../../../actions/api/groups'; 5 | 6 | import FormModal from '../modal/FormModal'; 7 | 8 | class RemoveBasePathModal extends Component { 9 | static propTypes = { 10 | groupName: PropTypes.string.isRequired, 11 | basePath: PropTypes.string.isRequired, 12 | removeBasePath: PropTypes.func.isRequired 13 | }; 14 | 15 | show() { 16 | this.refs.removeBasePath.show(); 17 | } 18 | 19 | render() { 20 | return ( 21 | 28 | Are you sure you want to remove this base path? 29 |
    30 |           {this.props.groupName}: {this.props.basePath}
    31 |         
    32 | Removing a base path will only remove the lock associated with that base 33 | path for Baragon requests. It will not alter the load balancer configuration 34 | in any way. 35 |
    36 | ); 37 | } 38 | } 39 | 40 | const mapDispatchToProps = (dispatch, ownProps) => ({ 41 | removeBasePath: () => dispatch(RemoveBasePath.trigger(ownProps.groupName, ownProps.basePath)) 42 | .then(response => (ownProps.then && ownProps.then(response))) 43 | }); 44 | 45 | export default connect( 46 | null, 47 | mapDispatchToProps, 48 | null, 49 | { withRef: true } 50 | )(RemoveBasePathModal); 51 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/UpdateInstanceButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | import { Glyphicon } from 'react-bootstrap'; 4 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 5 | import ToolTip from 'react-bootstrap/lib/Tooltip'; 6 | 7 | import { getClickComponent } from '../modal/ModalWrapper'; 8 | 9 | import UpdateInstanceModal from './UpdateInstanceModal'; 10 | 11 | const updateInstanceTooltip = ( 12 | 13 | Update 14 | 15 | ); 16 | 17 | export default class UpdateInstanceButton extends Component { 18 | static propTypes = { 19 | loadBalancer: PropTypes.string.isRequired, 20 | instanceId: PropTypes.string, 21 | action: PropTypes.oneOf(['add', 'remove']).isRequired, 22 | type: PropTypes.oneOf(['elb', 'alb']).isRequired, 23 | children: PropTypes.node, 24 | then: PropTypes.func 25 | }; 26 | 27 | static defaultProps = { 28 | children: ( 29 | 30 | 31 | 32 | 33 | 34 | ) 35 | }; 36 | 37 | render() { 38 | return ( 39 | 40 | {getClickComponent(this)} 41 | 49 | 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/RemoveUpstreamsButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; 4 | import ToolTip from 'react-bootstrap/lib/Tooltip'; 5 | 6 | import { getClickComponent } from '../modal/ModalWrapper'; 7 | 8 | import RemoveUpstreamsModal from './RemoveUpstreamsModal'; 9 | 10 | const removeTooltip = ( 11 | 12 | Remove all upstreams from this service 13 | 14 | ); 15 | 16 | export default class RemoveUpstreamsButton extends React.Component { 17 | static propTypes = { 18 | loadBalancerService: PropTypes.object, 19 | upstreams: PropTypes.arrayOf(PropTypes.shape({ 20 | group: PropTypes.string, 21 | rackId: PropTypes.string, 22 | requestId: PropTypes.string.isRequired, 23 | upstream: PropTypes.string.isRequired, 24 | })), 25 | children: PropTypes.node, 26 | afterRemoveUpstreams: PropTypes.func, 27 | }; 28 | 29 | static defaultProps = { 30 | children: ( 31 | 32 | 33 | Remove Upstreams 34 | 35 | 36 | ) 37 | }; 38 | 39 | render() { 40 | return ( 41 | 42 | {getClickComponent(this)} 43 | 49 | 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /BaragonUI/app/components/common/modalButtons/RemoveKnownAgentModal.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import { RemoveKnownAgent } from '../../../actions/api/groups'; 5 | 6 | import FormModal from '../modal/FormModal'; 7 | 8 | class RemoveKnownAgentModal extends Component { 9 | static propTypes = { 10 | groupName: PropTypes.string.isRequired, 11 | agentId: PropTypes.string.isRequired, 12 | removeKnownAgent: PropTypes.func.isRequired 13 | }; 14 | 15 | show() { 16 | this.refs.removeKnownAgentModal.show(); 17 | } 18 | 19 | render() { 20 | return ( 21 | 29 | Are you sure you want to delete this known agent? 30 |
    31 |           {this.props.groupName}: {this.props.agentId}
    32 |         
    33 | Deleting a known agent will remove it from the list of known agents for 34 | this load balancer group. It will NOT remove it from the list of active 35 | agents. 36 |
    37 | ); 38 | } 39 | } 40 | 41 | const mapDispatchToProps = (dispatch, ownProps) => ({ 42 | removeKnownAgent: () => dispatch(RemoveKnownAgent.trigger(ownProps.groupName, ownProps.agentId)) 43 | .then(response => (ownProps.then && ownProps.then(response))) 44 | }); 45 | 46 | export default connect( 47 | null, 48 | mapDispatchToProps, 49 | null, 50 | { withRef: true } 51 | )(RemoveKnownAgentModal); 52 | -------------------------------------------------------------------------------- /BaragonCore/src/main/java/com/hubspot/baragon/models/BaragonRequestKey.java: -------------------------------------------------------------------------------- 1 | package com.hubspot.baragon.models; 2 | 3 | public class BaragonRequestKey implements Comparable { 4 | private final String requestId; 5 | private final long updatedAt; 6 | 7 | public BaragonRequestKey(String requestId, long updatedAt) { 8 | this.requestId = requestId; 9 | this.updatedAt = updatedAt; 10 | } 11 | 12 | public String getRequestId() { 13 | return requestId; 14 | } 15 | 16 | public long getUpdatedAt() { 17 | return updatedAt; 18 | } 19 | 20 | @Override 21 | public int compareTo(BaragonRequestKey o) { 22 | return Long.compare(updatedAt, o.updatedAt); 23 | } 24 | 25 | @Override 26 | public boolean equals(Object o) { 27 | if (this == o) { 28 | return true; 29 | } 30 | if (o == null || getClass() != o.getClass()) { 31 | return false; 32 | } 33 | 34 | BaragonRequestKey that = (BaragonRequestKey) o; 35 | 36 | if (updatedAt != that.updatedAt) { 37 | return false; 38 | } 39 | if (requestId != null ? !requestId.equals(that.requestId) : that.requestId != null) { 40 | return false; 41 | } 42 | 43 | return true; 44 | } 45 | 46 | @Override 47 | public int hashCode() { 48 | int result = requestId != null ? requestId.hashCode() : 0; 49 | result = 31 * result + (int) (updatedAt ^ (updatedAt >>> 32)); 50 | return result; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return ( 56 | "BaragonRequestKey{" + 57 | "requestId='" + 58 | requestId + 59 | '\'' + 60 | ", updatedAt=" + 61 | updatedAt + 62 | '}' 63 | ); 64 | } 65 | } 66 | --------------------------------------------------------------------------------