diff:
@@ -98,7 +98,7 @@ LOG_LEVEL = os.getenv("LOG_LEVEL")
if LOG_LEVEL == 'DEBUG':
DEBUG = True
TEMPLATE_DEBUG = True
- ALLOWED_HOSTS = []
+ ALLOWED_HOSTS = ['*']
else:
ALLOWED_HOSTS = ['*']

diff:
@@ -38,7 +38,7 @@ DEBUG = True
TEMPLATE_DEBUG = True
-ALLOWED_HOSTS = []
+ALLOWED_HOSTS = ['*']
# Application definition

original_message: allow hosts when debug is true to let deploy-integ work (#635)


message:
fix for cluster capacity button disappear after django upgrade
keep using static instead of get_static_prefix
use double quote for consitency

change_type: MODIFY

diff:
<!-- Placed at the end of the document so the pages load faster (but it does not work ) -->
<script src="{% static "js/jquery-1.11.1.min.js" %}"></script>
<script src="{% static "js/bootstrap.min.js" %}"></script>
-<script src="{% static "js/deploy-board.js?changedate=2018.01.12.150000" %}"></script>
+<script src="{% static "js/deploy-board.js"%}?changedate=2018.01.12.150000"></script>
<script src="{% static "js/fuelux.min.js" %}"></script>
<script src="{% static "js/typeahead.bundle.min.js" %}"></script>
<script src="{% static "js/star-rating.min.js" %}"></script>

diff:
{% load static %}
{% block side-panel-actions %}
-
+

Capacity

{% endif %}
{% include "environs/env_tabs.tmpl" with envTabKind="config/capacity" %}
-
-
+
+

Basic Settings

diff:
{% endblock %}
{% load static %}
{% block side-panel-actions %}
-
+

Capacity

{% endblock %}
{% block main%}
{% include "environs/env_tabs.tmpl" with envTabKind="config/capacity" %}
-
-
+
+

diff:
{% if pinterest %}
-
+

{% verbatim %}

license: Apache License 2.0

repo: pinterest/teletraan

original_message:
fix for cluster capacity button disappear after django upgrade (#637)

keep using static instead of get_static_prefix

use double quote for consitency


message:
display warning message when find project name is default
add a new tmpl file for warning message
get stage for current page instead of from env
make sure check stage in current landing page
updated warning message
add null check for existing_stage_identifier

diff:
@@ -231,6 +231,8 @@ TELETRAAN_REDIRECT_CREATE_ENV_PAGE_URL = os.getenv("TELETRAAN_REDIRECT_CREATE_EN
IS_DURING_CODE_FREEZE = True if os.getenv("TELETRAAN_CODE_FREEZE", "false") == "true" else False
TELETRAAN_CODE_FREEZE_URL = os.getenv("TELETRAAN_CODE_FREEZE_URL", None)
TELETRAAN_JIRA_SOURCE_URL = os.getenv("TELETRAAN_JIRA_SOURCE_URL", None)
+TELETRAAN_TRANSFER_OWNERSHIP_URL = os.getenv("TELETRAAN_TRANSFER_OWNERSHIP_URL", None)
+TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL = os.getenv("TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL", None)
# use Rodimus if present
RODIMUS_SERVICE_URL = os.getenv("RODIMUS_SERVICE_URL", None)

change_type: ADD

diff:
+{% load utils %}
+{% load static %}
+
+

+ ×
+ No owner, no deploys
+
This teletraan stage does not belong to a project.

+
By February 26th, we will disable all deploys for any stages not owned by a project.

+
You can claim a stage here.
+
Visit here for more info. Thank you!

+
+

+
+

diff:
{% endif %}
{% endif %}
+{% if project_name_is_default %}
+ {% include "deploys/warning_no_nimbus_project_no_deploy.tmpl" %}
+{% endif %}
{% if request_feedback %}
{% include "feedbacks/feedback_banner.tmpl" %}

diff:
@@ -23,7 +23,7 @@ from django.http import HttpResponse
from django.contrib import messages
from deploy_board.settings import IS_PINTEREST
from deploy_board.settings import TELETRAAN_DISABLE_CREATE_ENV_PAGE, TELETRAAN_REDIRECT_CREATE_ENV_PAGE_URL,\
- IS_DURING_CODE_FREEZE, TELETRAAN_CODE_FREEZE_URL, TELETRAAN_JIRA_SOURCE_URL
+ IS_DURING_CODE_FREEZE, TELETRAAN_CODE_FREEZE_URL, TELETRAAN_JIRA_SOURCE_URL, TELETRAAN_TRANSFER_OWNERSHIP_URL,TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL
from deploy_board.settings import DISPLAY_STOPPING_HOSTS
from deploy_board.settings import GUINEA_PIG_ENVS
from deploy_board.settings import KAFKA_LOGGING_ADD_ON_ENVS
@@ -324,6 +324,18 @@ class EnvLandingView(View):
env_tag = tags_helper.get_latest_by_targe_id(request, env['id'])
basic_cluster_info = None
capacity_info = {'groups': groups}
+
+ project_name_is_default = False
+ stage_with_external_id = None
+ for env_stage in envs:
+ if env_stage['externalId'] is not None and env_stage['stageName'] == stage:
+ stage_with_external_id = env_stage
+ break
+
+ if stage_with_external_id is not None and stage_with_external_id['externalId'] is not None:
+ existing_stage_identifier = environs_helper.get_nimbus_identifier(stage_with_external_id['externalId'])
+ project_name_is_default = True if existing_stage_identifier is not None and existing_stage_identifier['projectName'] == "default" else False
+
if IS_PINTEREST:
basic_cluster_info = clusters_helper.get_cluster(request, env.get('clusterName'))
capacity_info['cluster'] = basic_cluster_info
@@ -355,6 +367,7 @@ class EnvLandingView(View):
"pinterest": IS_PINTEREST,
"csrf_token": get_token(request),
"display_stopping_hosts": DISPLAY_STOPPING_HOSTS,
+ "project_name_is_default": project_name_is_default,
})
showMode = 'complete'
sortByStatus = 'true'
@@ -388,6 +401,7 @@ class EnvLandingView(View):
"env_tag": env_tag,
"pinterest": IS_PINTEREST,
"display_stopping_hosts": DISPLAY_STOPPING_HOSTS,
+ "project_name_is_default": project_name_is_default,
}
sortByTag = request.GET.get('sortByTag', None)
if sortByTag:

diff:
"""Collection of all environs related calls
"""
from deploy_board.webapp.helpers.deployclient import DeployClient
-from deploy_board.settings import IS_PINTEREST
+from deploy_board.settings import IS_PINTEREST, TELETRAAN_TRANSFER_OWNERSHIP_URL, TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL
DEFAULT_ENV_SIZE = 30

original_message:
display warning message when find project name is default (#636)

add a new tmpl file for warning message

get stage for current page instead of from env

make sure check stage in current landing page

updated warning message

add null check for existing_stage_identifier


diff:
@@ -358,6 +358,8 @@ class EnvLandingView(View):
"disable_create_env_page": TELETRAAN_DISABLE_CREATE_ENV_PAGE,
"redirect_create_env_page_url": TELETRAAN_REDIRECT_CREATE_ENV_PAGE_URL,
"jira_source_url": TELETRAAN_JIRA_SOURCE_URL,
+ "transfer_ownership_url": TELETRAAN_TRANSFER_OWNERSHIP_URL,
+ "resource_ownership_wiki_url": TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL,
"groups": groups,
"capacity_hosts": capacity_hosts,
"provisioning_hosts": provisioning_hosts,
@@ -395,6 +397,8 @@ class EnvLandingView(View):
"code_freeze": IS_DURING_CODE_FREEZE,
"code_freeze_url": TELETRAAN_CODE_FREEZE_URL,
"jira_source_url": TELETRAAN_JIRA_SOURCE_URL,
+ "transfer_ownership_url": TELETRAAN_TRANSFER_OWNERSHIP_URL,
+ "resource_ownership_wiki_url": TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL,
"groups": groups,
"basic_cluster_info": basic_cluster_info,
"capacity_info": json.dumps(capacity_info),

diff:
"""Collection of all environs related calls
"""
from deploy_board.webapp.helpers.deployclient import DeployClient
-from deploy_board.settings import IS_PINTEREST, TELETRAAN_TRANSFER_OWNERSHIP_URL, TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL
+from deploy_board.settings import IS_PINTEREST
DEFAULT_ENV_SIZE = 30

original_message: add teletran transfer ownership url into render response (#640)


message:
only swallow exception when try to load warning message
print out exception detail

diff:
×
- No owner, no deploys
+ No project, no deploys

This teletraan stage does not belong to a project.

By February 26th, we will disable all deploys for any stages not owned by a project.

You can claim a stage here.

diff:
@@ -35,6 +35,7 @@ import random
import json
from helpers import builds_helper, environs_helper, agents_helper, ratings_helper, deploys_helper, \
systems_helper, environ_hosts_helper, clusters_helper, tags_helper, groups_helper, schedules_helper
+from helpers.exceptions import TeletraanException
import math
from dateutil.parser import parse
import calendar
@@ -333,8 +334,11 @@ class EnvLandingView(View):
break
if stage_with_external_id is not None and stage_with_external_id['externalId'] is not None:
+ try:
existing_stage_identifier = environs_helper.get_nimbus_identifier(stage_with_external_id['externalId'])
project_name_is_default = True if existing_stage_identifier is not None and existing_stage_identifier['projectName'] == "default" else False
+ except TeletraanException as detail:
+ log.error('Handling TeletraanException when trying to access nimbus API, error message {}'.format(detail))
if IS_PINTEREST:
basic_cluster_info = clusters_helper.get_cluster(request, env.get('clusterName'))

original_message:
only swallow exception when try to load warning message (#643)

print out exception detail


diff:
@@ -18,6 +18,7 @@ import logging
from deployd.types.ping_response import PingResponse
from deployd.common.decorators import singleton
from deployd.common.exceptions import AgentException
+requests.packages.urllib3.disable_warnings()
log = logging.getLogger(__name__)

diff:
@@ -18,6 +18,7 @@ from deployd.download.download_helper import DownloadHelper
import os
import requests
import logging
+requests.packages.urllib3.disable_warnings()
log = logging.getLogger(__name__)

diff:
@@ -16,6 +16,7 @@ import requests
from decorators import retry
from exceptions import NotAuthorizedException, TeletraanException, FailedAuthenticationException
+requests.packages.urllib3.disable_warnings()
DEFAULT_TIMEOUT = 30

diff:
@@ -4,6 +4,7 @@ from decorators import singleton
from deploy_board.settings import NIMBUS_SERVICE_URL, NIMBUS_SERVICE_VERSION
from exceptions import NotAuthorizedException, TeletraanException, FailedAuthenticationException
import requests
+requests.packages.urllib3.disable_warnings()
log = logging.getLogger(__name__)

diff:
@@ -23,6 +23,7 @@ from datetime import datetime
import pytz
import requests
import common
+requests.packages.urllib3.disable_warnings()
log = logging.getLogger(__name__)

diff:
@@ -25,6 +25,7 @@ import logging
import os
import requests
import traceback
+requests.packages.urllib3.disable_warnings()
from ngapptools.varnish.varnish_info import Recorder
from helpers.ngapp2_deploy import Ngapp2DeployUtils, NgappDeployStep

diff:
@@ -19,6 +19,7 @@ import time
import string
import random
import requests
+requests.packages.urllib3.disable_warnings()
def main():

original_message: fix for supress /usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/util/ssl_.py:100: InsecurePlatformWarning (#644)


message: replacing mimetype for Django upgrade

diff:
@@ -33,7 +33,7 @@ class ExceptionHandlerMiddleware(object):
# Error is displayed as a fragment over related feature area
if request.is_ajax():
ajax_vars = {'success': False, 'error': exception.message}
- return HttpResponse(json.dumps(ajax_vars), mimetype='application/javascript')
+ return HttpResponse(json.dumps(ajax_vars), content_type='application/javascript')
else:
# Not authorized
if isinstance(exception, NotAuthorizedException):

original_message: replacing mimetype for Django upgrade (#645)


message:
add delete identifier in nimbus when user delete a stage
delete stage before delete env and fix unloaded exception from create identifer function
raise exception instead od swallow it
remove teletraan exception import
add get all envs again after delete envs for get updated len of envs

diff:
@@ -832,7 +832,7 @@ def create_identifier_for_new_stage(request, env_name, stage_name):
else:
# retrieve Nimbus identifier for existing_stage
existing_stage_identifier = environs_helper.get_nimbus_identifier(stage_with_external_id['externalId'])
-
+ new_stage_identifier = None
# create Nimbus Identifier for the new stage
if existing_stage_identifier is not None:
nimbus_request_data = existing_stage_identifier.copy()
@@ -865,10 +865,18 @@ def post_add_stage(request, name):
def remove_stage(request, name, stage):
# TODO so we need to make sure the capacity is empty???
- environs_helper.delete_env(request, name, stage)
-
envs = environs_helper.get_all_env_stages(request, name)
+ current_env_stage_with_external_id = None
+ for env_stage in envs:
+ if env_stage['externalId'] is not None and env_stage['stageName'] == stage:
+ current_env_stage_with_external_id = env_stage
+ break
+
+ if current_env_stage_with_external_id is not None and current_env_stage_with_external_id['externalId'] is not None:
+ environs_helper.delete_nimbus_identifier(current_env_stage_with_external_id['externalId'])
+ environs_helper.delete_env(request, name, stage)
+ envs = environs_helper.get_all_env_stages(request, name)
response = redirect('/env/' + name)
if len(envs) == 0:

diff:
@@ -66,6 +66,9 @@ def get_nimbus_identifier(name):
def create_nimbus_identifier(data):
return nimbusclient.create_one_identifier(data)
+def delete_nimbus_identifier(name):
+ return nimbusclient.delete_one_identifier(name)
+
# Teletraan Deploy client helpers

diff:
@@ -56,3 +56,7 @@ class NimbusClient(object):
response = requests.post('{}/api/{}/identifiers'.format(NIMBUS_SERVICE_URL, NIMBUS_SERVICE_VERSION), json=payload)
return self.handle_response(response)
+
+ def delete_one_identifier(self, name):
+ response = requests.delete('{}/api/{}/identifiers/{}'.format(NIMBUS_SERVICE_URL, NIMBUS_SERVICE_VERSION, name))
+ return self.handle_response(response)
\ No newline at end of file

original_message:
add delete identifier in nimbus when user delete a stage (#642)

delete stage before delete env and fix unloaded exception from create identifer function

raise exception instead od swallow it

remove teletraan exception import

add get all envs again after delete envs for get updated len of envs


message:
create a new function to update image name and image value for ebs compute host
remove unnecessary ajax call and add same logic to cluster configuration page

diff:
v-on:baseimagechange="baseImageChange" v-on:imagenamechange="imageNameChange" v-bind:inadvanced="inAdvanced" showhelp="true" v-on:helpclick="baseImageHelpClick">
</baseimage-select>
<base-image-help v-show="showBaseImageHelp" v-bind:data="baseImageHelpData"></base-image-help>
- <label-select label="Host Type" title="Compute Capability of the host" v-model="selectedHostTypeValue" v-bind:selectoptions="hostTypeOptions" showhelp="true" v-on:helpclick="hostTypeHelpClick"></label-select>
+ <label-select label="Host Type" title="Compute Capability of the host" v-model="selectedHostTypeValue" v-bind:selectoptions="hostTypeOptions"
+ v-on:input="updateImageNameForEbsHost" showhelp="true" v-on:helpclick="hostTypeHelpClick"></label-select>
<hostype-help v-show="showHostTypeHelp" v-bind:data="hostTypeHelpData"></hostype-help>
<label-select label="Security Zone" title="Security zone to control inbound/outboud traffic" v-model="selectedSecurityZoneValue" v-bind:selectoptions="securityZones"
showhelp="true" v-on:helpclick="securityZoneHelpClick"></label-select>
}
});
},
+ updateImageNameForEbsHost: function () {
+ var cell = capacitySetting.currentCell;
+ var scope = this;
+ var provider = capacitySetting.currentProvider;
+ value = 'cmp_base-ebs';
+ if (this.selectedHostTypeValue.startsWith('EbsCompute')) {
+ capacitySetting.imageNameValue = value;
+ capacitySetting.imagenames = info.baseImageNames.map(
+ function (o) {
+ return {
+ value: o,
+ text: o,
+ isSelected: o === value
+ }
+ }),
+ scope.imageNameChange(value);
+
+ //Grab all images for this image name
+ $.ajax({
+ type: 'GET',
+ url: location.protocol + '//' + location.host + '/clouds/get_base_images/' + value + '?cell=' + cell,
+ dataType: "json",
+ beforeSend: function (xhr, settings) {
+ var csrftoken = getCookie('csrftoken')
+ xhr.setRequestHeader("X-CSRFToken", csrftoken);
+
+ },
+ success: function (data) {
+ sorted = data.sort(function (item1, item2) { return item2.publish_date - item1.publish_date; })
+ capacitySetting.baseimages = sorted.map(function (o) { return { value: o.id, text: o.provider_name } })
+ capacitySetting.baseImageValue = sorted[0].id
+ },
+ error: function (data) {
+ globalNotificationBanner.error = data
+ }
+ });
+ }
+ },
placementsHelpClick: function(){
if (this.showPlacementsHelp){
this.showPlacementsHelp = false

diff:
</baseimage-select>
<base-image-help v-show="showBaseImageHelp" v-bind:data="baseImageHelpData"></base-image-help>
<label-select label="Host Type" title="Compute Capability of the host" v-model="selectedHostTypeValue" v-bind:selectoptions="hostTypeOptions"
- showhelp="true" v-on:helpclick="hostTypeHelpClick"></label-select>
+ v-on:input="updateImageNameForEbsHost" showhelp="true" v-on:helpclick="hostTypeHelpClick"></label-select>
<hostype-help v-show="showHostTypeHelp" v-bind:data="hostTypeHelpData"></hostype-help>
<label-select label="Security Zone" title="Security zone to control inbound/outboud traffic" showhelp="true" v-model="selectedSecurityZoneValue"
v-bind:selectoptions="securityZones" v-on:helpclick="securityZoneHelpClick"></label-select>
@@ -368,6 +368,44 @@ var capacitySetting = new Vue({
}
});
},
+ updateImageNameForEbsHost: function () {
+ var cell = capacitySetting.currentCell;
+ var scope = this;
+ var provider = capacitySetting.currentProvider;
+ value = 'cmp_base-ebs';
+ if (this.selectedHostTypeValue.startsWith('EbsCompute')) {
+ capacitySetting.imageNameValue = value;
+ capacitySetting.imagenames = info.baseImageNames.map(
+ function (o) {
+ return {
+ value: o,
+ text: o,
+ isSelected: o === value
+ }
+ }),
+ scope.imageNameChange(value);
+
+ //Grab all images for this image name
+ $.ajax({
+ type: 'GET',
+ url: location.protocol + '//' + location.host + '/clouds/get_base_images/' + value + '?cell=' + cell,
+ dataType: "json",
+ beforeSend: function (xhr, settings) {
+ var csrftoken = getCookie('csrftoken')
+ xhr.setRequestHeader("X-CSRFToken", csrftoken);
+
+ },
+ success: function (data) {
+ sorted = data.sort(function (item1, item2) { return item2.publish_date - item1.publish_date; })
+ capacitySetting.baseimages = sorted.map(function (o) { return { value: o.id, text: o.provider_name } })
+ capacitySetting.baseImageValue = sorted[0].id
+ },
+ error: function (data) {
+ globalNotificationBanner.error = data
+ }
+ });
+ }
+ },
placementsHelpClick: function(){
if (this.showPlacementsHelp){
this.showPlacementsHelp = false

original_message:
create a new function to update image name and image value for ebs compute host (#647)

remove unnecessary ajax call and add same logic to cluster configuration page


message:
fix for convert string true to boolean true for assign public ip
make assign public ip readonly in user data configs

diff:
@@ -156,7 +156,7 @@ function getDefaultPlacement(capacityCreationInfo) {
//for each abstract_name, we have one candidate.
//If existingItems is null or empty, the abstract_name
//will be the existing one
- var arr = assignPublicIp ? this.cmpPublic : this.cmpPrivate
+ var arr = assignPublicIp == 'true' ? this.cmpPublic : this.cmpPrivate
var fullArr = assignPublicIp ? this.allPublic : this.allPrivate
if (existingItems != null && existingItems.length > 0) {
@@ -205,7 +205,7 @@ function getDefaultPlacement(capacityCreationInfo) {
},
getFullList: function (assignPublicIp, existingItems) {
- var arr = assignPublicIp ? this.allPublic : this.allPrivate
+ var arr = assignPublicIp == 'true' ? this.allPublic : this.allPrivate
if (existingItems != null && existingItems.length > 0) {
existingItems = existingItems.map(function (item) {
var fullInfo = arr.find(

diff:
return {
name: key,
value: currentCluster.configs[key],
- readonly: key === 'spiffe_id'
+ readonly: key === 'spiffe_id' || key === 'assign_public_ip'
}
}),
- assignPublicIP: currentCluster.configs.assign_public_ip ? true : false,
+ assignPublicIP: currentCluster.configs.assign_public_ip == "true" ? true : false,
awsRole: currentCluster.configs.aws_role,
baseimages: info.baseImages.map(function(o) {
var acceptance = o.acceptance ? ' [' + o.acceptance + ']' : '';

original_message:
fix for convert string true to boolean true for assign public ip (#649)

make assign public ip readonly in user data configs


message:
make sure user cannot add assign_public_ip and spiffe_id from add user data config
add alert when input spiffe id and assign publi ip names

diff:
@@ -99,6 +99,15 @@ Vue.component('data-config-field', {
}
});
+Vue.component('data-config-readonly-field', {
+ template: '
\
+ {{name}}\
+
\
+ <input class="form-control" type="text" v-bind:value="value" v-bind:readonly="readonly ? true : false"></input>\
+
\
+
',
+ props: ['name', 'value', 'readonly']
+});
Vue.component('aws-user-data', {
template: '
\
@@ -124,6 +133,23 @@ Vue.component('aws-user-data', {
}
});
+Vue.component('aws-readonly-user-data', {
+ template: '
\
+ <data-config-readonly-field v-for="data in alluserdata" v-bind:name="data.name" v-bind:value="data.value" v-bind:readonly="data.readonly" v-bind:inadvanced="inadvanced"\
+ v-show="shouldShow(data.name)"></data-config-readonly-field>\
+
',
+ props: ['alluserdata', 'inadvanced', 'showcmpgroup'],
+ methods: {
+ shouldShow: function (name) {
+ if (this.inadvanced) {
+ return this.showcmpgroup ? true : name != "cmp_group"
+ }
+ else {
+ return !name.startsWith("pinfo_") && name != "cmp_group"
+ }
+ }
+ }
+});
Vue.component('add-config-button', {
template: '<button type="button" class="deployToolTip btn btn-default btn-sm" data-toggle="modal" v-bind:data-target="target">\
@@ -198,8 +224,13 @@ Vue.component('aws-config-modal', {
},
addConfig: function () {
if (this.useCustomizedName) {
+ if (this.customizedName !='spiffe_id' && this.customizedName!='assign_public_ip'){
this.$emit('click', { name: this.customizedName, value: this.selectedOptionValue })
}
+ else {
+ alert("teletraan currently do not allow user to modify spiffeId and assign public ip")
+ }
+ }
else {
this.$emit('click', { name: this.selectedOption.name, value: this.selectedOptionValue })
}

diff:
</div>
</div>
<aws-user-data v-bind:alluserdata="allUserData" v-on:deleteconfig="deleteConfig" v-on:change="configChange" v-bind:inadvanced="inAdvanced" v-bind:showcmpgroup="showCmpGroup"></aws-user-data>
+ <aws-readonly-user-data v-bind:alluserdata="textUserData" v-bind:inadvanced="inAdvanced" v-bind:showcmpgroup="showCmpGroup"></aws-readonly-user-data>
</div>
</fieldset>
</form>
return {
name: key,
value: currentCluster.configs[key],
- readonly: key === 'spiffe_id' || key === 'assign_public_ip'
}
- }),
+ }).filter(function(item){return item.name!='spiffe_id' && item.name!='assign_public_ip'}),
+ textUserData: Object.keys(currentCluster.configs).map(
+ function(key) {
+ return {
+ name: key,
+ value: currentCluster.configs[key],
+ readonly: true
+ }
+ }).filter(function(item){return item.name ==='spiffe_id' || item.name ==='assign_public_ip'}),
assignPublicIP: currentCluster.configs.assign_public_ip == "true" ? true : false,
awsRole: currentCluster.configs.aws_role,
baseimages: info.baseImages.map(function(o) {
text: key,
default: info.configList[key]
}
- })
+ }).filter(function(item){return item.name!='assign_public_ip'})
if (this.inAdvanced){
return options;
}

diff:
</div>
</div>
<aws-user-data v-bind:alluserdata="allUserData" v-on:deleteconfig="deleteConfig" v-on:change="configChange" inadvanced="inAdvanced"></aws-user-data>
+ <aws-readonly-user-data v-bind:alluserdata="textUserData" inadvanced="inAdvanced"></aws-readonly-user-data>
</div>
</fieldset>
</form>
@@ -107,6 +108,14 @@ var capacitySetting = new Vue({
value: info.defaultCMPConfigs[key]
}
}).filter(function(item){return item.name!='cmp_group'}),
+ textUserData: Object.keys(info.configList).map(
+ function(key) {
+ return {
+ name: key,
+ value: info.configList[key],
+ readonly: true
+ }
+ }).filter(function(item){return item.name ==='assign_public_ip'}),
assignPublicIP: false,
baseImageHelpData: [],
baseimages: info.baseImages.map(
@@ -126,7 +135,7 @@ var capacitySetting = new Vue({
text: key,
default: info.configList[key]
}
- }),
+ }).filter(function(item){return item.name != 'assign_public_ip'}),
confirmDialogTitle: "Confirm New Capacity Creation",
confirmDialogId: "createHostGroup",
currentProvider: info.defaultProvider,

original_message:
make sure user cannot add assign_public_ip and spiffe_id from add user data config (#650)

add alert when input spiffe id and assign publi ip names


diff:
@@ -228,7 +228,7 @@ Vue.component('aws-config-modal', {
this.$emit('click', { name: this.customizedName, value: this.selectedOptionValue })
}
else {
- alert("teletraan currently do not allow user to modify spiffeId and assign public ip")
+ alert("teletraan currently do not allow user to modify spiffe_id and assign_public_ip")
}
}
else {

original_message: make sure alert messages matches to actual filed names (#651)


message:
enabled Add button and provide red error message when user add spiffe_id and assign_public_ip
adjust error message to below customer name text box
adjust error message to the left
adjust error message
modify the error message content to spiffe_id and assign_public_ip separately

diff:
@@ -158,8 +158,9 @@ Vue.component('add-config-button', {
props: ['target']
});
-Vue.component('aws-config-modal', {
- template: '<div class="modal fade" v-bind:id="id" tabindex="-1" role="dialog"\
+Vue.component("aws-config-modal", {
+ template:
+ '
\

\

\
@@ -185,7 +186,9 @@ Vue.component('aws-config-modal', {

\
Name\

\
- <input type="text" class="form-control"placeholder="name..." v-model="customizedName">\
+ <input type="text" class="form-control"placeholder="name..." v-on:keyup="checkReadOnlyValue" v-model="customizedName">\
+
{{errorMessage}}
\

\

\

\
@@ -197,14 +200,16 @@ Vue.component('aws-config-modal', {

\

\

\
- <button type="button" class="btn btn-primary" data-dismiss="modal" v-on:click="addConfig">Add</button>\
+ <button type="button" class="btn btn-primary" data-dismiss="modal" v-on:click="addConfig" id="disableConfigButton">Add</button>\
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>\

\
</form>

',
- props: ['options', 'id'],
+ props: ["options", "id"],
data: function() {
return {
useCustomizedName: false,
+ shouldShowError: false,
+ errorMessage: "",
selectedOption: this.options[0],
selectedOptionValue: this.options[0].default,
selectedValue: this.options[0].name,
@@ -213,39 +218,72 @@ Vue.component('aws-config-modal', {
},
methods: {
updateValue: function(value) {
- this.selectedValue = value
+ this.selectedValue = value;
if (value === "") {
- this.selectedOptionValue = ""
- }
- else {
- this.selectedOption = this.options.filter(function (item) { return item.name === value })[0];
+ this.selectedOptionValue = "";
+ } else {
+ this.selectedOption = this.options.filter(function(item) {
+ return item.name === value;
+ })[0];
this.selectedOptionValue = this.selectedOption.default;
}
},
addConfig: function() {
if (this.useCustomizedName) {
- if (this.customizedName !='spiffe_id' && this.customizedName!='assign_public_ip'){
- this.$emit('click', { name: this.customizedName, value: this.selectedOptionValue })
- }
- else {
- alert("teletraan currently do not allow user to modify spiffe_id and assign_public_ip")
+ if (
+ this.customizedName != "spiffe_id" &&
+ this.customizedName != "assign_public_ip"
+ ) {
+ this.$emit("click", {
+ name: this.customizedName,
+ value: this.selectedOptionValue
+ });
+ } else {
+ document.getElementById(
+ "disableConfigButton"
+ ).disabled = true;
+ }
+ } else {
+ this.$emit("click", {
+ name: this.selectedOption.name,
+ value: this.selectedOptionValue
+ });
}
+ },
+ checkReadOnlyValue: function () {
+ if (this.useCustomizedName) {
+ if (this.customizedName === "spiffe_id" ||
+ this.customizedName === "assign_public_ip"
+ ) {
+ document.getElementById(
+ "disableConfigButton"
+ ).disabled = true;
+ this.shouldShowError = true;
+ if (this.customizedName === "spiffe_id") {
+ this.errorMessage =
+ this.customizedName + " cannot be added. it will be auto generated";
+ } else {
+ this.errorMessage =
+ this.customizedName + " cannot be added. we only allow assign_public_ip checkbox to modify the value";
+ }
+ } else {
+ document.getElementById(
+ "disableConfigButton"
+ ).disabled = false;
+ this.shouldShowError = false;
}
- else {
- this.$emit('click', { name: this.selectedOption.name, value: this.selectedOptionValue })
}
},
toggleCustomizedName: function(checked) {
if (checked) {
- this.selectedOption = null
- this.selectedValue = ""
- this.useCustomizedName = true
- }
- else {
- this.useCustomizedName = true
- this.customizedName = ""
- }
- this.selectedOptionValue = null
+ this.selectedOption = null;
+ this.selectedValue = "";
+ this.useCustomizedName = true;
+ } else {
+ this.useCustomizedName = true;
+ this.customizedName = "";
+ }
+ this.selectedOptionValue = null;
}
}
});

original_message:
enabled Add button and provide red error message when user add spiffe_id and assign_public_ip (#654)

adjust error message to below customer name text box

adjust error message to the left

adjust error message

modify the error message content to spiffe_id and assign_public_ip separately


message:
fix for spiffe id disappear when user create new cluster
make sure user cannot save data config if they manually modified spiffe_id from UI

diff:
@@ -99,16 +99,6 @@ Vue.component('data-config-field', {
}
});
-Vue.component('data-config-readonly-field', {
- template: '
\
- {{name}}\
-
\
- <input class="form-control" type="text" v-bind:value="value" v-bind:readonly="readonly ? true : false"></input>\
-
\
-
',
- props: ['name', 'value', 'readonly']
-});
-
Vue.component('aws-user-data', {
template: '
\
<data-config-field v-for="data in alluserdata" v-bind:name="data.name" v-bind:value="data.value" v-bind:readonly="data.readonly" v-bind:inadvanced="inadvanced"\
@@ -133,24 +123,6 @@ Vue.component('aws-user-data', {
}
});
-Vue.component('aws-readonly-user-data', {
- template: '
\
- <data-config-readonly-field v-for="data in alluserdata" v-bind:name="data.name" v-bind:value="data.value" v-bind:readonly="data.readonly" v-bind:inadvanced="inadvanced"\
- v-show="shouldShow(data.name)"></data-config-readonly-field>\
-
',
- props: ['alluserdata', 'inadvanced', 'showcmpgroup'],
- methods: {
- shouldShow: function (name) {
- if (this.inadvanced) {
- return this.showcmpgroup ? true : name != "cmp_group"
- }
- else {
- return !name.startsWith("pinfo_") && name != "cmp_group"
- }
- }
- }
-});
-
Vue.component('add-config-button', {
template: '<button type="button" class="deployToolTip btn btn-default btn-sm" data-toggle="modal" v-bind:data-target="target">\
 Add Config\

diff:
</div>
</div>
<aws-user-data v-bind:alluserdata="allUserData" v-on:deleteconfig="deleteConfig" v-on:change="configChange" v-bind:inadvanced="inAdvanced" v-bind:showcmpgroup="showCmpGroup"></aws-user-data>
- <aws-readonly-user-data v-bind:alluserdata="textUserData" v-bind:inadvanced="inAdvanced" v-bind:showcmpgroup="showCmpGroup"></aws-readonly-user-data>
</div>
</fieldset>
</form>
return {
name: key,
value: currentCluster.configs[key],
+ readonly: key === 'spiffe_id' || key === 'assign_public_ip'
}
- }).filter(function(item){return item.name!='spiffe_id' && item.name!='assign_public_ip'}),
- textUserData: Object.keys(currentCluster.configs).map(
- function(key) {
- return {
- name: key,
- value: currentCluster.configs[key],
- readonly: true
- }
- }).filter(function(item){return item.name ==='spiffe_id' || item.name ==='assign_public_ip'}),
+ }),
+ spiffeId: 'spiffe_id' in currentCluster.configs ? currentCluster.configs['spiffe_id']:null,
assignPublicIP: currentCluster.configs.assign_public_ip == "true" ? true : false,
awsRole: currentCluster.configs.aws_role,
baseimages: info.baseImages.map(function(o) {
return false
}
+ //Validate spiffe_id not modified by user
+ if ('spiffe_id' in clusterInfo.configs && clusterInfo.configs['spiffe_id'] != this.spiffeId){
+ globalNotificationBanner.error = "spiffe id cannot be modified by user"
+ return false
+ }
+
if (imageNameValue.startsWith('cmp_base')){
if (!('pinfo_role' in clusterInfo.configs && clusterInfo.configs['pinfo_role']==='cmp_base')){
globalNotificationBanner.error = "cmp_base image must have pinfo_role set to cmp_base"

diff:
</div>
</div>
<aws-user-data v-bind:alluserdata="allUserData" v-on:deleteconfig="deleteConfig" v-on:change="configChange" inadvanced="inAdvanced"></aws-user-data>
- <aws-readonly-user-data v-bind:alluserdata="textUserData" inadvanced="inAdvanced"></aws-readonly-user-data>
</div>
</fieldset>
</form>
@@ -108,14 +107,6 @@ var capacitySetting = new Vue({
value: info.defaultCMPConfigs[key]
}
}).filter(function(item){return item.name!='cmp_group'}),
- textUserData: Object.keys(info.configList).map(
- function(key) {
- return {
- name: key,
- value: info.configList[key],
- readonly: true
- }
- }).filter(function(item){return item.name ==='assign_public_ip'}),
assignPublicIP: false,
baseImageHelpData: [],
baseimages: info.baseImages.map(

original_message:
fix for spiffe id disappear when user create new cluster (#655)

make sure user cannot save data config if they manually modified spiffe_id from UI


message:
fix Assign public ip checkbox bug
hide assign_public_ip field from user data config list
remove assign public ip from all user data list since we use checkbox to assgin public ip

diff:
return {
name: key,
value: currentCluster.configs[key],
- readonly: key === 'spiffe_id' || key === 'assign_public_ip'
+ readonly: key === 'spiffe_id'
}
- }),
+ }).filter(function(item){return item.name!='assign_public_ip'}),
spiffeId: 'spiffe_id' in currentCluster.configs ? currentCluster.configs['spiffe_id']:null,
assignPublicIP: currentCluster.configs.assign_public_ip == "true" ? true : false,
awsRole: currentCluster.configs.aws_role,
if (this.assignPublicIP){
clusterInfo.configs['assign_public_ip'] = true
}
+ else{
+ clusterInfo.configs['assign_public_ip'] = false
+ }
if (!this.inAdvanced){
clusterInfo.configs['aws_role'] = this.awsRole
}

diff:
@@ -196,7 +196,9 @@ var capacitySetting = new Vue({
if (this.assignPublicIP){
clusterInfo.configs['assign_public_ip'] = true
}
-
+ else {
+ clusterInfo.configs['assign_public_ip'] = false
+ }
if (this.validateInput(clusterInfo)){
//Send request
this.sendRequest(clusterInfo);

diff:
@@ -537,7 +537,9 @@ var capacitySetting = new Vue({
if (this.assignPublicIP){
clusterInfo.configs['assign_public_ip'] = true
}
-
+ else {
+ clusterInfo.configs['assign_public_ip'] = false
+ }
if (this.validateInput(clusterInfo)) {
//Send request
this.sendRequest(clusterInfo);

original_message:
fix Assign public ip checkbox bug (#656)

hide assign_public_ip field from user data config list

remove assign public ip from all user data list since we use checkbox to assgin public ip


message:
add phobos link for each env in agent
using customer filter instead of additional for loop
add is pinterest check for host details display

diff:
{% include "panel_heading.tmpl" with panel_title="Agent Details" panel_body_id="agentDetailsId" direction="down" copy_host_name_button=True %}

-
{% for agent_wrapper in agent_wrappers %}
- {% with env=agent_wrapper.env agent=agent_wrapper.agent %}
- {% include "hosts/host_details.tmpl" with agent=agent env=env%}
+ {% with env=agent_wrapper.env agent=agent_wrapper.agent hostdetailsvalue=host_details.items|lookup:"Phobos Link" %}
+ {% include "hosts/host_details.tmpl" with agent=agent env=env hostdetailsvalue=hostdetailsvalue%}
{% endwith %}
{% endfor %}
-

{% endif %}

diff:
{{ agent.failCount }}

- Error Message
+ Error Message
+ {% if pinterest %}
+ Full deploy log for {{env.envName}}
+ {% endif %}
+

{{ agent_wrapper.error }}

diff:
@@ -1044,3 +1044,7 @@ def convertConfigHistoryString(change):
except:
pass
return change
+
+@register.filter(name='lookup')
+def getPhoboLink(mapping, key):
+ return dict(mapping)[key]

original_message:
add phobos link for each env in agent (#657)

using customer filter instead of additional for loop

add is pinterest check for host details display


message:
using codahale.metrics to expose commits and deploys api metrics
emit actual deploys from envDeploys class
update instrument
add more instrumentation places for teletraan apis
remove instrumentations from EnvStages and Environs apis
remove extra spaces
remove instrumentations from daily count api

diff:
*/
package com.pinterest.teletraan.resource;
+import com.codahale.metrics.annotation.ExceptionMetered;
+import com.codahale.metrics.annotation.Timed;
import com.google.common.base.Optional;
import com.pinterest.deployservice.bean.CommitBean;
@@ -45,6 +47,8 @@ public class Commits {
}
@GET
+ @Timed
+ @ExceptionMetered
@Path("{repo : [a-zA-Z0-9\\-_]+}/{sha : [a-zA-Z0-9\\-_]+}")
@ApiOperation(
value = "Get commit infos",
@@ -64,6 +68,8 @@ public class Commits {
* It is recommended to call multiple times (pagination) with size < 30 to avoid timeout
*/
@GET
+ @Timed
+ @ExceptionMetered
public List<CommitBean> getCommits(@QueryParam("repo") String repo,
@QueryParam("startSha") String startSha, @QueryParam("endSha") String endSha,
@QueryParam("size") Optional<Integer> size, @QueryParam("path") Optional<String> path) throws Exception {

diff:
*/
package com.pinterest.teletraan.resource;
+import com.codahale.metrics.annotation.ExceptionMetered;
+import com.codahale.metrics.annotation.Timed;
import com.google.common.base.Optional;
@@ -84,6 +86,8 @@ public class Deploys {
}
@GET
+ @Timed
+ @ExceptionMetered
@Path("/{id : [a-zA-Z0-9\\-_]+}")
@ApiOperation(
value = "Get deploy info",
@@ -136,6 +140,8 @@ public class Deploys {
}
@PUT
+ @Timed
+ @ExceptionMetered
@Path("/{id : [a-zA-Z0-9\\-_]+}")
@ApiOperation(
value = "Update deploy",
@@ -156,6 +162,8 @@ public class Deploys {
}
@DELETE
+ @Timed
+ @ExceptionMetered
@Path("/{id : [a-zA-Z0-9\\-_]+}")
@ApiOperation(
value = "Delete deploy info",

diff:
*/
package com.pinterest.teletraan.resource;
+import com.codahale.metrics.annotation.ExceptionMetered;
+import com.codahale.metrics.annotation.Timed;
+
import com.pinterest.deployservice.bean.*;
import com.pinterest.deployservice.common.Constants;
import com.pinterest.deployservice.dao.DeployDAO;
@@ -78,6 +81,8 @@ public class EnvDeploys {
}
@GET
+ @Timed
+ @ExceptionMetered
@Path("/current")
@ApiOperation(
value = "Get deploy info by environment",
@@ -91,6 +96,8 @@ public class EnvDeploys {
}
@POST
+ @Timed
+ @ExceptionMetered
@Path("/current/actions")
@ApiOperation(
value = "Take deploy action",
@@ -138,6 +145,8 @@ public class EnvDeploys {
}
@PUT
+ @Timed
+ @ExceptionMetered
@Path("/hostactions")
@ApiOperation(
value = "Take a deploy action",
@@ -177,6 +186,8 @@ public class EnvDeploys {
}
@POST
+ @Timed
+ @ExceptionMetered
@ApiOperation(
value = "Create a deploy",
notes = "Creates a deploy given an environment name, stage name, build id and description",
@@ -204,6 +215,8 @@ public class EnvDeploys {
// Even though this is PUT, but it really just update progress, no check is needed
@PUT
+ @Timed
+ @ExceptionMetered
@Path("/current/progress")
@ApiOperation(
value = "Update deploy progress",

original_message:
using codahale.metrics to expose commits and deploys api metrics (#659)

emit actual deploys from envDeploys class

update instrument

add more instrumentation places for teletraan apis

remove instrumentations from EnvStages and Environs apis

remove extra spaces

remove instrumentations from daily count api


diff:
@@ -112,8 +112,9 @@ def get_host_details(host_id):
}
if IS_PINTEREST and PHOBOS_URL:
host_ip = instance['config']['internal_address']
+ host_name = instance['config']['name']
if host_ip is not None:
- phobos_link = PHOBOS_URL + host_ip
+ phobos_link = PHOBOS_URL + host_name
host_details['Phobos Link'] = phobos_link
return host_details

original_message: update phobos link using host name instead of host ip for security requirement (#662)


diff:
@@ -234,6 +234,8 @@ TELETRAAN_CODE_FREEZE_URL = os.getenv("TELETRAAN_CODE_FREEZE_URL", None)
TELETRAAN_JIRA_SOURCE_URL = os.getenv("TELETRAAN_JIRA_SOURCE_URL", None)
TELETRAAN_TRANSFER_OWNERSHIP_URL = os.getenv("TELETRAAN_TRANSFER_OWNERSHIP_URL", None)
TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL = os.getenv("TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL", None)
+TELETRAAN_WARNING_MESSAGE_URL = os.getenv("TELETRAAN_WARNING_MESSAGE_URL", None)
+TELETRAAN_WARNING_MESSAGE_TITLE = os.getenv("TELETRAAN_WARNING_MESSAGE_TITLE", None)
# use Rodimus if present
RODIMUS_SERVICE_URL = os.getenv("RODIMUS_SERVICE_URL", None)

diff:
+{% load utils %}
+{% load static %}
+
+

+ ×
+
{{warning_message_title}}

+
Visit here for more info. Thank you!

+

+
+

diff:
{% endif %}
{% endif %}
+{% if pinterest %}
+ {% if warning_message_title %}
+ {% include "deploys/deploy_warning_message.tmpl" %}
+ {% endif %}
+{% endif %}
+
{% if project_name_is_default %}
{% include "deploys/warning_no_nimbus_project_no_deploy.tmpl" %}
{% endif %}

diff:
@@ -23,7 +23,7 @@ from django.http import HttpResponse
from django.contrib import messages
from deploy_board.settings import IS_PINTEREST
from deploy_board.settings import TELETRAAN_DISABLE_CREATE_ENV_PAGE, TELETRAAN_REDIRECT_CREATE_ENV_PAGE_URL,\
- IS_DURING_CODE_FREEZE, TELETRAAN_CODE_FREEZE_URL, TELETRAAN_JIRA_SOURCE_URL, TELETRAAN_TRANSFER_OWNERSHIP_URL,TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL
+ IS_DURING_CODE_FREEZE, TELETRAAN_CODE_FREEZE_URL, TELETRAAN_JIRA_SOURCE_URL, TELETRAAN_TRANSFER_OWNERSHIP_URL,TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL, TELETRAAN_WARNING_MESSAGE_URL, TELETRAAN_WARNING_MESSAGE_TITLE
from deploy_board.settings import DISPLAY_STOPPING_HOSTS
from deploy_board.settings import GUINEA_PIG_ENVS
from deploy_board.settings import KAFKA_LOGGING_ADD_ON_ENVS
@@ -374,6 +374,8 @@ class EnvLandingView(View):
"csrf_token": get_token(request),
"display_stopping_hosts": DISPLAY_STOPPING_HOSTS,
"project_name_is_default": project_name_is_default,
+ "warning_message_title": TELETRAAN_WARNING_MESSAGE_TITLE,
+ "warning_message_url": TELETRAAN_WARNING_MESSAGE_URL,
})
showMode = 'complete'
sortByStatus = 'true'
@@ -410,6 +412,8 @@ class EnvLandingView(View):
"pinterest": IS_PINTEREST,
"display_stopping_hosts": DISPLAY_STOPPING_HOSTS,
"project_name_is_default": project_name_is_default,
+ "warning_message_title": TELETRAAN_WARNING_MESSAGE_TITLE,
+ "warning_message_url": TELETRAAN_WARNING_MESSAGE_URL,
}
sortByTag = request.GET.get('sortByTag', None)
if sortByTag:

original_message: adding warning message title and url to deployment page (#664)


diff:
{% endif %}
+ {% if group_info == None %}

<button id="launchInstance" class="deployToolTip btn btn-default btn-block"
data-toggle="modal" title="Launch Instances">
 Launch Instance
</button>

+ {% endif %}

<button id="terminateAllHosts" class="deployToolTip btn btn-default btn-block"
data-toggle="modal" title="Terminate All Hosts">

original_message: remove launch config button for ASG groups (#666)


message: remove banner message in teletraan

diff:
@@ -234,8 +234,6 @@ TELETRAAN_CODE_FREEZE_URL = os.getenv("TELETRAAN_CODE_FREEZE_URL", None)
TELETRAAN_JIRA_SOURCE_URL = os.getenv("TELETRAAN_JIRA_SOURCE_URL", None)
TELETRAAN_TRANSFER_OWNERSHIP_URL = os.getenv("TELETRAAN_TRANSFER_OWNERSHIP_URL", None)
TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL = os.getenv("TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL", None)
-TELETRAAN_WARNING_MESSAGE_URL = os.getenv("TELETRAAN_WARNING_MESSAGE_URL", None)
-TELETRAAN_WARNING_MESSAGE_TITLE = os.getenv("TELETRAAN_WARNING_MESSAGE_TITLE", None)
# use Rodimus if present
RODIMUS_SERVICE_URL = os.getenv("RODIMUS_SERVICE_URL", None)

change_type: DELETE

diff:
-{% load utils %}
-{% load static %}
-
-

- ×
-
{{warning_message_title}}

-
Visit here for more info. Thank you!

-

-
-

diff:
{% endif %}
{% endif %}
-{% if pinterest %}
- {% if warning_message_title %}
- {% include "deploys/deploy_warning_message.tmpl" %}
- {% endif %}
-{% endif %}
-
{% if project_name_is_default %}
{% include "deploys/warning_no_nimbus_project_no_deploy.tmpl" %}
{% endif %}

diff:
{% endif %}
- {% if group_info == None %}

<button id="launchInstance" class="deployToolTip btn btn-default btn-block"
data-toggle="modal" title="Launch Instances">
 Launch Instance
</button>

- {% endif %}

<button id="terminateAllHosts" class="deployToolTip btn btn-default btn-block"
data-toggle="modal" title="Terminate All Hosts">

diff:
@@ -23,7 +23,7 @@ from django.http import HttpResponse
from django.contrib import messages
from deploy_board.settings import IS_PINTEREST
from deploy_board.settings import TELETRAAN_DISABLE_CREATE_ENV_PAGE, TELETRAAN_REDIRECT_CREATE_ENV_PAGE_URL,\
- IS_DURING_CODE_FREEZE, TELETRAAN_CODE_FREEZE_URL, TELETRAAN_JIRA_SOURCE_URL, TELETRAAN_TRANSFER_OWNERSHIP_URL,TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL, TELETRAAN_WARNING_MESSAGE_URL, TELETRAAN_WARNING_MESSAGE_TITLE
+ IS_DURING_CODE_FREEZE, TELETRAAN_CODE_FREEZE_URL, TELETRAAN_JIRA_SOURCE_URL, TELETRAAN_TRANSFER_OWNERSHIP_URL,TELETRAAN_RESOURCE_OWNERSHIP_WIKI_URL
from deploy_board.settings import DISPLAY_STOPPING_HOSTS
from deploy_board.settings import GUINEA_PIG_ENVS
from deploy_board.settings import KAFKA_LOGGING_ADD_ON_ENVS
@@ -374,8 +374,6 @@ class EnvLandingView(View):
"csrf_token": get_token(request),
"display_stopping_hosts": DISPLAY_STOPPING_HOSTS,
"project_name_is_default": project_name_is_default,
- "warning_message_title": TELETRAAN_WARNING_MESSAGE_TITLE,
- "warning_message_url": TELETRAAN_WARNING_MESSAGE_URL,
})
showMode = 'complete'
sortByStatus = 'true'
@@ -412,8 +410,6 @@ class EnvLandingView(View):
"pinterest": IS_PINTEREST,
"display_stopping_hosts": DISPLAY_STOPPING_HOSTS,
"project_name_is_default": project_name_is_default,
- "warning_message_title": TELETRAAN_WARNING_MESSAGE_TITLE,
- "warning_message_url": TELETRAAN_WARNING_MESSAGE_URL,
}
sortByTag = request.GET.get('sortByTag', None)
if sortByTag:

original_message: remove banner message in teletraan (#669)


message:
add UI support for target groups ARNs
update message to be more readable
modify groups to group in message

diff:
- Load Balancers
+ Classic Load Balancers

Please enter the load balancers you would like to attach to this group seperated by commas.

+

+
+ Target Group ARNs
+
+

+

+ <input class="form-control" id="target_groups" name="target_groups" required="false"
+ type="text" value="{{ config.targetGroups |default_if_none:'' }}"/>
+
+ <button id="targetgroupsExpressionId" class="deployToolTip btn btn-default"
+ type="button" data-toggle="tooltip"
+ title="click for more information on target groups">
+
+ </button>
+
+

+

+

+

+

+

+

+ Please enter the target group ARNs you would like to attach to this autoscaling group seperated by commas.
+

+

</fieldset>
{% csrf_token %}
</form>
$("#loadbalancersExpressionId").click(function() {
$("#loadbalancersExpressionDetailsId").collapse('toggle');
});
+ $("#targetgroupsExpressionId").click(function() {
+ $("#targetgroupsExpressionDetailsId").collapse('toggle');
+ });
});
</script>

diff:
@@ -219,6 +219,7 @@ def update_group_config(request, group_name):
groupRequest["pagerRecipients"] = params.get("pager_recipients")
groupRequest["launchLatencyTh"] = int(params["launch_latency_th"]) * 60
groupRequest["loadBalancers"] = params.get("load_balancers")
+ groupRequest["targetGroups"] = params.get("target_groups")
if "healthcheck_state" in params:
groupRequest["healthcheckState"] = True

original_message:
add UI support for target groups ARNs (#670)

update message to be more readable

modify groups to group in message


message:
kill ngapp2 deploy page
add back general url
remove ngapp2 from recent environments list and remove all /ngapp2/ urls

diff:
{% load static %}

- {% if isPinterest %}
- ngapp2
- {% endif %}
{% for envName in envNames %}
{% if envName != "ngapp2-B" and envName != "ngapp2-A" %}
{{ envName }}

diff:
@@ -27,7 +27,6 @@ if IS_PINTEREST:
url(r'^', include('deploy_board.webapp.urls')),
url(r'^', include('deploy_board.webapp.mix_urls')),
url(r'^', include('deploy_board.webapp.arcee_urls')),
- url(r'^', include('deploy_board.webapp.ngapp2_urls')),
url(r'^', include('deploy_board.webapp.cluster_urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
else:

original_message:
kill ngapp2 deploy page (#672)

add back general url

remove ngapp2 from recent environments list and remove all /ngapp2/ urls


message:
Do not allow customers to pass in spiffe_id during post call update cluster
do not allow user to pass in spiffe id
adjust log info message
throw exception when user is trying to change spiffe_id in upgrade_cluster and create_cluster_with_env
update typo

diff:
@@ -30,7 +30,7 @@ import logging
from helpers import baseimages_helper, hosttypes_helper, securityzones_helper, placements_helper, \
autoscaling_groups_helper, groups_helper, cells_helper
from helpers import clusters_helper, environs_helper, environ_hosts_helper
-from helpers.exceptions import NotAuthorizedException
+from helpers.exceptions import NotAuthorizedException, TeletraanException
import common
import traceback
@@ -76,6 +76,10 @@ class EnvCapacityBasicCreateView(View):
cluster_info = json.loads(request.body)
log.info("Create Capacity in the provider")
+ if 'spiffe_id' in cluster_info:
+ log.error("Teletraan does not support user to change spiffe_id %s" % cluster_info['spiffe_id'])
+ raise TeletraanException("Teletraan does not support user to create spiffe_id")
+
clusters_helper.create_cluster_with_env(request, cluster_name, name, stage, cluster_info)
log.info("Associate cluster_name to environment")
@@ -220,8 +224,18 @@ class ClusterConfigurationView(View):
cluster_name = env.get('clusterName')
cluster_info = json.loads(request.body)
log.info("Update Cluster Configuration with {}", cluster_info)
- image = baseimages_helper.get_by_id(
- request, cluster_info['baseImageId'])
+ cluster_name = '{}-{}'.format(name, stage)
+ current_cluster = clusters_helper.get_cluster(request, cluster_name)
+ log.info("getting current Cluster Configuration is {}", current_cluster)
+ if 'spiffe_id' not in current_cluster and 'spiffe_id' in cluster_info:
+ log.error("Teletraan does not support user to pass in customized spiffe_id %s if exising cluster does not have" % cluster_info['spiffe_id'])
+ raise TeletraanException("Teletraan does not support user to pass in customized spiffe_id")
+ if 'spiffe_id' in current_cluster and 'spiffe_id' in cluster_info:
+ if current_cluster['spiffe_id'] != cluster_info['spiffe_id']:
+ log.error("Teletraan does not support user to change spiffe_id %s , current spiffe_id %s"
+ % (cluster_info['spiffe_id'], current_cluster['spiffe_id']))
+ raise TeletraanException("Teletraan does not support user to change spiffe_id")
+ image = baseimages_helper.get_by_id(request, cluster_info['baseImageId'])
clusters_helper.update_cluster(request, cluster_name, cluster_info)
except NotAuthorizedException as e:
log.error("Have an NotAuthorizedException error {}".format(e))

original_message:
Do not allow customers to pass in spiffe_id during post call update cluster (#677)

do not allow user to pass in spiffe id

adjust log info message

throw exception when user is trying to change spiffe_id in upgrade_cluster and create_cluster_with_env

update typo


message:
add a new flag in build db to protect deploy only happens from trusted artifact build
move new flag into EnvBean instead of BuildBean
correct buildBean and EnvBean settings
update test
update warning message to be more clear

diff:
@@ -61,7 +61,8 @@ import java.io.Serializable;
* schedule_id VARCHAR(22) DEFAULT NULL,
* deploy_constraint_id VARCHAR(22) DEFAULT NULL,
* external_id CHAR(36),
- * allow_private_build TINYINT(1) DEFAULT 0
+ * allow_private_build TINYINT(1) DEFAULT 0,
+ * ensure_trusted_build TINYINT(1) DEFAULT 0,
* <p>
* PRIMARY KEY (env_id)
* );
@@ -184,6 +185,9 @@ public class EnvironBean implements Updatable, Serializable {
@JsonProperty("allowPrivateBuild")
private Boolean allow_private_build;
+ @JsonProperty("ensureTrustedBuild")
+ private Boolean ensure_trusted_build;
+
public String getWebhooks_config_id() {
return webhooks_config_id;
}
@@ -488,6 +492,14 @@ public class EnvironBean implements Updatable, Serializable {
this.allow_private_build = allow_private_build;
}
+ public Boolean getEnsure_trusted_build() {
+ return ensure_trusted_build;
+ }
+
+ public void setEnsure_trusted_build(Boolean ensure_trusted_build) {
+ this.ensure_trusted_build = ensure_trusted_build;
+ }
+
@Override
public SetClause genSetClause() {
SetClause clause = new SetClause();
@@ -529,6 +541,7 @@ public class EnvironBean implements Updatable, Serializable {
clause.addColumn("deploy_constraint_id", deploy_constraint_id);
clause.addColumn("external_id", external_id);
clause.addColumn("allow_private_build", allow_private_build);
+ clause.addColumn("ensure_trusted_build", ensure_trusted_build);
return clause;
}

diff:
@@ -41,6 +41,7 @@ CREATE TABLE IF NOT EXISTS environs (
deploy_constraint_id VARCHAR(22),
external_id CHAR(36),
allow_private_build TINYINT(1) NOT NULL DEFAULT 0,
+ ensure_trusted_build TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (env_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE UNIQUE INDEX env_name_stage_idx ON environs (env_name, stage_name);

diff:
@@ -1076,6 +1076,7 @@ public class DBDAOTest {
envBean.setMax_parallel_rp(1);
envBean.setOverride_policy(OverridePolicy.OVERRIDE);
envBean.setAllow_private_build(false);
+ envBean.setEnsure_trusted_build(false);
return envBean;
}

diff:
@@ -219,6 +219,11 @@ public class EnvDeploys {
throw new TeletaanInternalException(Response.Status.BAD_REQUEST, String.format("This stage does not allow deploying a private build. Please Contact #teletraan and #security-related to whitelist your stage for deploying private build"));
}
+ // check if the build is from a trusted artifact url
+ if(!buildBean.getArtifact_url().startsWith("https://soxrepo.pinadmin.com") && envBean.getEnsure_trusted_build()) {
+ throw new TeletaanInternalException(Response.Status.BAD_REQUEST, String.format("This build is not allowed to deploy. Please Contact #teletraan and #security-related to ensure the build artifact is published to a trusted url"));
+ }
+
String deployId = deployHandler.deploy(envBean, buildId, description, operator);
LOG.info("Successfully create deploy {} for env {}/{} by {}.", deployId, envName, stageName, operator);

diff:
+-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+-- ALWAYS BACKUP YOUR DATA BEFORE EXECUTING THIS SCRIPT
+-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+# This script upgrade DB schema from version 5 to version 6
+
+ALTER TABLE environs ADD COLUMN ensure_trusted_build TINYINT(1) NOT NULL DEFAULT 0;
+
+-- make sure to update the schema version to 5
+UPDATE schema_versions SET version=6;
\ No newline at end of file

original_message:
add a new flag in build db to protect deploy only happens from trusted artifact build (#693)

move new flag into EnvBean instead of BuildBean

correct buildBean and EnvBean settings

update test

update warning message to be more clear


message:
use default cmp_base-eb-18.04
update capacity default to use 18.04 image type

diff:
@@ -286,7 +286,7 @@ if IS_PINTEREST:
DEFAULT_PROVIDER = 'AWS'
#Pinterest Default AMI image name
- DEFAULT_CMP_IMAGE = 'cmp_base-ebs'
+ DEFAULT_CMP_IMAGE = 'cmp_base-ebs-18.04'
#Pinterest Default Host Type
DEFAULT_CMP_HOST_TYPE = 'EbsComputeLo(Recommended)'

diff:
@@ -372,7 +372,7 @@ var capacitySetting = new Vue({
var cell = capacitySetting.currentCell;
var scope = this;
var provider = capacitySetting.currentProvider;
- value = 'cmp_base-ebs';
+ value = 'cmp_base-ebs-18.04';
if (this.selectedHostTypeValue.startsWith('EbsCompute')) {
capacitySetting.imageNameValue = value;
capacitySetting.imagenames = info.baseImageNames.map(

original_message:
use default cmp_base-eb-18.04 (#696)

update capacity default to use 18.04 image type


diff:
@@ -286,7 +286,7 @@ if IS_PINTEREST:
DEFAULT_PROVIDER = 'AWS'
#Pinterest Default AMI image name
- DEFAULT_CMP_IMAGE = 'cmp_base-ebs-18.04'
+ DEFAULT_CMP_IMAGE = 'cmp_base-ebs'
#Pinterest Default Host Type
DEFAULT_CMP_HOST_TYPE = 'EbsComputeLo(Recommended)'

diff:
@@ -372,7 +372,7 @@ var capacitySetting = new Vue({
var cell = capacitySetting.currentCell;
var scope = this;
var provider = capacitySetting.currentProvider;
- value = 'cmp_base-ebs-18.04';
+ value = 'cmp_base-ebs';
if (this.selectedHostTypeValue.startsWith('EbsCompute')) {
capacitySetting.imageNameValue = value;
capacitySetting.imagenames = info.baseImageNames.map(

diff:
@@ -31,7 +31,7 @@ urlpatterns = [
cluster_view.get_security_zones_by_provider_and_cell),
url(r'^clouds/get_base_images/$', cluster_view.get_base_images_by_name),
url(r'^clouds/get_base_images/(?P<name>[a-zA-Z0-9\-_\.]+)/$', cluster_view.get_base_images_by_name_json),
- url(r'^clouds/get_base_image_info/(?P<name>[a-zA-Z0-9\-_]+)/$', cluster_view.get_base_images_by_name_json),
+ url(r'^clouds/get_base_image_info/(?P<name>[a-zA-Z0-9\-_\.]+)/$', cluster_view.get_base_images_by_name_json),
url(r'^clouds/create_host_type/$', cluster_view.create_host_type),
url(r'^clouds/hosttypes/$', cluster_view.get_host_types),

original_message: fix for get base image info for ebs 18.04 (#697)


message: set default image type as 18.04

original_message: set default image type as 18.04 (#698)


message:
Disable creating new group
* update readme file
* disable create new group
revert readme

diff:
$(".chosen-select").chosen({"width": "100%"});
$('#saveGroupConfigBtnId').attr('disabled','disabled');
-
- $('#launchConfigIdFormId input').keyup(function() {
- $('#saveGroupConfigBtnId').removeAttr('disabled');
- });
-
- $('#launchConfigIdFormId textarea').keyup(function() {
- $('#saveGroupConfigBtnId').removeAttr('disabled');
- });
-
- $('#launchConfigIdFormId select').change(function() {
- $('#saveGroupConfigBtnId').removeAttr('disabled');
- });
-
- $('#launchConfigIdFormId input').change(function() {
- $('#saveGroupConfigBtnId').removeAttr('disabled');
- });
});
</script>

original_message:
Disable creating new group (#701)

* update readme file

* disable create new group

revert readme


message: damage plot feature implementation

diff:
@@ -135,6 +135,7 @@ namespace HunterPie.Core
public bool ShowOnlyTimer { get; set; } = false;
public bool ShowTimer { get; set; } = true;
public double Width { get; set; } = 1;
+ public bool EnableDamagePlot { get; set; } = true;
}
public class Players

diff:
<Image Source="{StaticResource ICON_STOPWATCH}" Width="20" Height="20" Margin="5,0,0,0"/>
<TextBlock x:Name="Timer" Width="295" Foreground="#FFEAEAEA" FontWeight="Normal" FontSize="16" Padding="3,4,0,4" />
</StackPanel>
+ <p:DamagePlot x:Name="DamagePlot" MaxHeight="100" />
<WrapPanel x:Name="Party" Visibility="Visible" Orientation="Horizontal" Canvas.Top="29">
</WrapPanel>
</StackPanel>

diff:
@@ -46,6 +46,7 @@ namespace HunterPie.GUI.Widgets.DPSMeter
{
Context = ctx.Player.PlayerParty;
GameContext = ctx;
+ DamagePlot.SetContext(ctx);
HookEvents();
}
@@ -115,6 +116,7 @@ namespace HunterPie.GUI.Widgets.DPSMeter
player.UnhookEvents();
}
Players.Clear();
+ DamagePlot.Dispose();
Players = null;
GameContext = null;
Context = null;
@@ -212,6 +214,7 @@ namespace HunterPie.GUI.Widgets.DPSMeter
TimerVisibility = UserSettings.PlayerConfig.Overlay.DPSMeter.ShowTimer ? Visibility.Visible : Visibility.Collapsed;
+ DamagePlot.ApplySettings();
UpdatePlayersColor();
ScaleWidget(UserSettings.PlayerConfig.Overlay.DPSMeter.Scale, UserSettings.PlayerConfig.Overlay.DPSMeter.Scale);
@@ -279,20 +282,29 @@ namespace HunterPie.GUI.Widgets.DPSMeter
private void SnapWidget(double newWidth)
{
double temp;
- if (newWidth / (322 * DefaultScaleX) % 1 >= 0.5)
+ const int defaultWidth = 322;
+ const int memberItemHeight = 46;
+ const int plotHeight = 100;
+
+ if (newWidth / (defaultWidth * DefaultScaleX) % 1 >= 0.5)
{
- temp = (322 * DefaultScaleX) * Math.Ceiling(newWidth / (322 * DefaultScaleX));
+ temp = (defaultWidth * DefaultScaleX) * Math.Ceiling(newWidth / (defaultWidth * DefaultScaleX));
} else
{
- temp = (322 * DefaultScaleX) * Math.Floor(newWidth / (322 * DefaultScaleX));
+ temp = (defaultWidth * DefaultScaleX) * Math.Floor(newWidth / (defaultWidth * DefaultScaleX));
}
- if (newWidth < (322 * DefaultScaleX))
+ if (newWidth < (defaultWidth * DefaultScaleX))
{
- temp = (322 * DefaultScaleX);
+ temp = (defaultWidth * DefaultScaleX);
}
- else if (newWidth > (322 * DefaultScaleX) * 4) temp = (322 * DefaultScaleX) * 4;
+ else if (newWidth > (defaultWidth * DefaultScaleX) * 4) temp = (defaultWidth * DefaultScaleX) * 4;
Width = temp;
- MaxHeight = MinHeight = (TimerContainer.ActualHeight + (Party.ActualHeight == 0 ? 46 * 4 : Party.ActualHeight) + 2) * DefaultScaleY;
+ MaxHeight = MinHeight = (
+ TimerContainer.ActualHeight
+ + (Party.ActualHeight == 0 ? memberItemHeight * 4 : Party.ActualHeight)
+ + 2
+ + (UserSettings.PlayerConfig.Overlay.DPSMeter.EnableDamagePlot ? plotHeight : 0)
+ ) * DefaultScaleY;
}
}
}

diff:
<custom:Switcher x:Name="switchEnableTimer" Text="{Binding Mode=OneTime, XPath=/Strings/Client/Settings/String[@ID\=\'ENABLE_TIMER\']/@Name}" Margin="10,0,10,5" ToolTip="{Binding Mode=OneTime, XPath=/Strings/Client/Settings/String[@ID\=\'ENABLE_TIMER_DESC\']/@Name}" RestartVisibility="Collapsed"/>
<custom:Switcher x:Name="switchEnableTimerInExpeditions" Text="{Binding Mode=OneTime, XPath=/Strings/Client/Settings/String[@ID\=\'ENABLE_TIMER_IN_GL\']/@Name}" Margin="10,0,10,5" ToolTip="{Binding Mode=OneTime, XPath=/Strings/Client/Settings/String[@ID\=\'ENABLE_TIMER_IN_GL_DESC\']/@Name}" RestartVisibility="Collapsed"/>
<custom:Switcher x:Name="switchEnableOnlyTimer" Text="{Binding Mode=OneTime, XPath=/Strings/Client/Settings/String[@ID\=\'ENABLE_ONLY_TIMER\']/@Name}" Margin="10,0,10,5" ToolTip="{Binding Mode=OneTime, XPath=/Strings/Client/Settings/String[@ID\=\'ENABLE_ONLY_TIMER_DESC\']/@Name}" RestartVisibility="Collapsed"/>
+ <custom:Switcher x:Name="switchEnableDamagePlot" Text="{Binding Mode=OneTime, XPath=/Strings/Client/Settings/String[@ID\=\'ENABLE_DAMAGE_PLOT\']/@Name}" Margin="10,0,10,5" ToolTip="{Binding Mode=OneTime, XPath=/Strings/Client/Settings/String[@ID\=\'ENABLE_DAMAGE_PLOT_DESC\']/@Name}" RestartVisibility="Collapsed"/>
<custom:MinimalSlider x:Name="DamageBackgroundOpacity" Text="{Binding XPath=/Strings/Client/Settings/String[@ID\=\'STATIC_BACKGROUND_OPACITY\']/@Name}" MinChange="0.1" MinValue="0" MaxValue="1" ValueText="{Binding Value, ElementName=DamageBackgroundOpacity, StringFormat=\{0:P\}}" Foreground="WhiteSmoke" FontFamily="Segoe UI Light" Margin="10,0,10,5"/>
<custom:MinimalSlider x:Name="DamageComponentOpacity" Text="{Binding XPath=/Strings/Client/Settings/String[@ID\=\'STATIC_OPACITY\']/@Name}" MinChange="0.1" MinValue="0" MaxValue="1" ValueText="{Binding Value, ElementName=DamageComponentOpacity, StringFormat=\{0:P\}}" Foreground="WhiteSmoke" FontFamily="Segoe UI Light" Margin="10,0,10,5"/>
<custom:ColorPicker NameText="{Binding Mode=OneTime, XPath=/Strings/Client/Settings/String[@ID\=\'STATIC_METER_PLAYER_1\']/@Name}" x:Name="FirstPlayerColor" Margin="10,0,10,5"/>

diff:
@@ -171,6 +171,7 @@ namespace HunterPie.GUIControls
settingsUI.switchEnableTimerInExpeditions.IsEnabled = settings.Overlay.DPSMeter.ShowTimerInExpeditions;
settingsUI.switchEnableOnlyTimer.IsEnabled = settings.Overlay.DPSMeter.ShowOnlyTimer;
settingsUI.switchEnableOnlyMyself.IsEnabled = settings.Overlay.DPSMeter.ShowOnlyMyself;
+ settingsUI.switchEnableDamagePlot.IsEnabled = settings.Overlay.DPSMeter.EnableDamagePlot;
settingsUI.DamageMeterPosition.X = settings.Overlay.DPSMeter.Position[0];
settingsUI.DamageMeterPosition.Y = settings.Overlay.DPSMeter.Position[1];
settingsUI.FirstPlayerColor.Color = settings.Overlay.DPSMeter.PartyMembers[0].Color;
@@ -314,6 +315,7 @@ namespace HunterPie.GUIControls
settings.Overlay.DPSMeter.ShowDPSWheneverPossible = settingsUI.switchEnableDPSWheneverPossible.IsEnabled;
settings.Overlay.DPSMeter.ShowTimer = settingsUI.switchEnableTimer.IsEnabled;
settings.Overlay.DPSMeter.ShowTimerInExpeditions = settingsUI.switchEnableTimerInExpeditions.IsEnabled;
+ settings.Overlay.DPSMeter.EnableDamagePlot = settingsUI.switchEnableDamagePlot.IsEnabled;
settings.Overlay.DPSMeter.ShowOnlyTimer = settingsUI.switchEnableOnlyTimer.IsEnabled;
settings.Overlay.DPSMeter.ShowOnlyMyself = settingsUI.switchEnableOnlyMyself.IsEnabled;
settings.Overlay.DPSMeter.Position[0] = settingsUI.DamageMeterPosition.X;

diff:
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
+ <Page Include="GUI\Widgets\DPSMeter\Parts\DamagePlot.xaml" >
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
<Page Include="GUI\Widgets\DPSMeter\Parts\PartyMember.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
<Compile Include="GUI\Widgets\DPSMeter\Meter.xaml.cs">
<DependentUpon>Meter.xaml</DependentUpon>
</Compile>
+ <Compile Include="GUI\Widgets\DPSMeter\Parts\DamagePlot.xaml.cs" >
+ <DependentUpon>DamagePlot.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="GUI\Widgets\DPSMeter\Parts\MemberPlotModel.cs" />
<Compile Include="GUI\Widgets\DPSMeter\Parts\PartyMember.xaml.cs">
<DependentUpon>PartyMember.xaml</DependentUpon>
</Compile>
<PackageReference Include="Newtonsoft.Json">
<Version>12.0.3</Version>
</PackageReference>
+ <PackageReference Include="OxyPlot.Wpf">
+ <Version>2.0.0</Version>
+ </PackageReference>
</ItemGroup>
<ItemGroup>
<None Include="clean.py">

diff:
<String ID="STATIC_METER_PLAYER_3" Name="Third Player"/>
<String ID="STATIC_METER_PLAYER_4" Name="Fourth Player"/>
<String ID="STATIC_METER_POSITION_DESC" Name="Changes the Damage Meter widget position."/>
+ <String ID="ENABLE_DAMAGE_PLOT" Name="Show damage plot"/>
+ <String ID="ENABLE_DAMAGE_PLOT_DESC" Name="If enabled, damage plot will be visible."/>
<!-- Abnormalities Widget -->
<String ID="STATIC_CREATE_ABNORM_BAR_DESC" Name="Create a new abnormality bar."/>
<String ID="STATIC_REMOVE_ABNORM_BAR_DESC" Name="Remove last crated abnormality bar."/>

license: MIT License

original_message: damage plot feature implementation


message: damage plot: fix for null context

diff:
@@ -45,7 +45,7 @@ namespace HunterPie.GUI.Widgets.DPSMeter.Parts
}
Timer.Interval = TimeSpan.FromMilliseconds(UserSettings.PlayerConfig.Overlay.DPSMeter.DamagePlotPollInterval);
- if (!Timer.IsEnabled && !Context.Player.InPeaceZone)
+ if (Context?.Player != null && !Timer.IsEnabled && Context.Player.InPeaceZone)
{
Timer.Start();
}

original_message: damage plot: fix for null context


message:
Improved plugin installation experience
display plugin version on installation
don't display "plugin updated" on start when no new updates available
added "Restart now" button on installation notification
display message when plugin is up-to-date already

diff:
@@ -79,17 +79,22 @@ namespace HunterPie.Plugins
if (PluginUpdate.PluginSupportsUpdate(modInformation))
{
- if (await PluginUpdate.UpdateAllFiles(modInformation, module))
+ switch (await PluginUpdate.UpdateAllFiles(modInformation, module))
{
+ case UpdateResult.Updated:
serializedModule = File.ReadAllText(Path.Combine(module, "module.json"));
modInformation = JsonConvert.DeserializeObject<PluginInformation>(serializedModule);
- Debugger.Module($"Updated plugin: {modInformation.Name}");
- }
- else
- {
+ Debugger.Module($"Updated plugin: {modInformation.Name} (ver {modInformation.Version})");
+ break;
+
+ case UpdateResult.Failed:
Debugger.Error($"Failed to update plugin: {modInformation.Name}");
continue;
+
+ case UpdateResult.UpToDate:
+ Debugger.Module($"Plugin {modInformation.Name} is up-to-date (ver {modInformation.Version})");
+ break;
}
}

diff:
@@ -14,23 +14,25 @@ namespace HunterPie.Plugins
return !string.IsNullOrEmpty(pluginInformation.Update.UpdateUrl);
}
- public static async Task<bool> UpdateAllFiles(PluginInformation pInformation, string modPath)
+ public static async Task<UpdateResult> UpdateAllFiles(PluginInformation pInformation, string modPath)
{
string onlineSerializedInformation = await ReadOnlineModuleJson(pInformation.Update.UpdateUrl);
if (onlineSerializedInformation is null)
{
//Debugger.Error($"Failed to update plugin: {pInformation.Name}!");
- return false;
+ return UpdateResult.Failed;
}
PluginInformation onlineInformation = JsonConvert.DeserializeObject<PluginInformation>(onlineSerializedInformation);
if (!(Hunterpie.ParseVersion(Hunterpie.HUNTERPIE_VERSION) >= Hunterpie.ParseVersion(onlineInformation.Update.MinimumVersion)))
{
- return false;
+ return UpdateResult.Failed;
}
+ var result = UpdateResult.UpToDate;
+
foreach (string filePath in onlineInformation.Update.FileHashes.Keys)
{
string onlineHash = onlineInformation.Update.FileHashes[filePath];
@@ -51,8 +53,10 @@ namespace HunterPie.Plugins
if (!(await DownloadFileAsync(updateurl, outputPath, filePath)))
{
- return false;
+ return UpdateResult.Failed;
}
+
+ result = UpdateResult.Updated;
}
}
else
@@ -61,11 +65,15 @@ namespace HunterPie.Plugins
string outputPath = Path.Combine(modPath, filePath);
if (!(await DownloadFileAsync(updateurl, outputPath, filePath)))
{
- return false;
+ return UpdateResult.Failed;
}
+ result = UpdateResult.Updated;
}
}
- return await DownloadFileAsync($"{pInformation.Update.UpdateUrl}/module.json", Path.Combine(modPath, "module.json"), "module.json");
+
+ return await DownloadFileAsync($"{pInformation.Update.UpdateUrl}/module.json", Path.Combine(modPath, "module.json"), "module.json")
+ ? result
+ : UpdateResult.Failed;
}
public static async Task<bool> DownloadFileAsync(string URL, string output, string relFilepath)

diff:
<Compile Include="GUIControls\Settings.xaml.cs">
<DependentUpon>Settings.xaml</DependentUpon>
</Compile>
+ <Compile Include="HunterPie.Plugins\UpdateResult.cs" />
<Compile Include="Utils\FloatExtensions.cs" />
<None Include="address\MonsterHunterWorld.420540.map">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>

diff:
@@ -1117,28 +1117,53 @@ namespace HunterPie
if (PluginUpdate.PluginSupportsUpdate(moduleInformation))
{
- if (await PluginUpdate.UpdateAllFiles(moduleInformation, modPath))
+ switch (await PluginUpdate.UpdateAllFiles(moduleInformation, modPath))
{
- Debugger.Module($"Installed {moduleInformation.Name}!");
- } else
+ case UpdateResult.Updated:
+ Debugger.Module($"Installed {moduleInformation.Name}! (ver {moduleInformation.Version})");
+
+ CNotification notification = new CNotification
{
+ Text = GStrings.GetLocalizationByXPath("/Console/String[@ID='MESSAGE_PLUGIN_INSTALLED']").Replace("{name}", moduleInformation.Name),
+ NIcon = FindResource("ICON_PLUGIN") as ImageSource,
+
+ FirstButtonVisibility = Visibility.Visible,
+ Callback1 = Reload,
+ FirstButtonImage = FindResource("ICON_RESTART") as ImageSource,
+ FirstButtonText = GStrings.GetLocalizationByXPath("/Notifications/String[@ID='STATIC_RESTART']"),
+
+ SecondButtonVisibility = Visibility.Collapsed,
+ ShowTime = 15
+ };
+ NotificationsPanel.Children.Add(notification);
+ notification.ShowNotification();
+ break;
+
+ case UpdateResult.Failed:
Debugger.Error($"Failed to install {moduleInformation.Name}.");
- }
- } else
- {
- Debugger.Error(GStrings.GetLocalizationByXPath("/Console/String[@ID='ERROR_PLUGIN_AUTO_UPDATE']"));
- }
+ return;
- CNotification notification = new CNotification()
+ case UpdateResult.UpToDate:
+ string uptodateText =
+ $"Plugin {moduleInformation.Name} is already installed and up-to-date! (ver {moduleInformation.Version})";
+ Debugger.Module(uptodateText);
+ CNotification uptodateNotification = new CNotification
{
- Text = GStrings.GetLocalizationByXPath("/Console/String[@ID='MESSAGE_PLUGIN_INSTALLED']").Replace("{name}", moduleInformation.Name),
+ Text = uptodateText,
NIcon = FindResource("ICON_PLUGIN") as ImageSource,
+
FirstButtonVisibility = Visibility.Collapsed,
SecondButtonVisibility = Visibility.Collapsed,
ShowTime = 5
};
- NotificationsPanel.Children.Add(notification);
- notification.ShowNotification();
+ NotificationsPanel.Children.Add(uptodateNotification);
+ uptodateNotification.ShowNotification();
+ return;
+ }
+ } else
+ {
+ Debugger.Error(GStrings.GetLocalizationByXPath("/Console/String[@ID='ERROR_PLUGIN_AUTO_UPDATE']"));
+ }
}
private void window_DragEnter(object sender, DragEventArgs e)

diff:
<String ID="STATIC_OPENFOLDER" Name="Open Folder"/>
<String ID="MESSAGE_BUILD_UPLOADED" Name="Build uploaded to Honey Hunters World!"/>
<String ID="MESSAGE_GEAR_EXPORTED" Name="Exported gear to ./DataExport!"/>
+ <String ID="STATIC_RESTART" Name="Restart now"/>
</Notifications>
<RichPresence>
<String ID="RPC_DESCRIPTION_IN_MAIN_MENU" Name="In Main Menu"/>

original_message:
Improved plugin installation experience
- display plugin version on installation
- don't display "plugin updated" on start when no new updates available
- added "Restart now" button on installation notification
- display message when plugin is up-to-date already


message: install reporting support, changed plugin subtext formatting, update registry url

diff:
@@ -209,7 +209,7 @@ namespace HunterPie.Core
public double PosX { get; set; } = 0;
public double PosY { get; set; } = 0;
public PluginProxyMode PluginProxyMode { get; set; } = PluginProxyMode.Auto;
- public string PluginRegistryUrl { get; set; } = "https://plugins.hunterpie.herokuapp.com/";
+ public string PluginRegistryUrl { get; set; } = "https://hunterpie-plugins.herokuapp.com/";
public bool MinimizeAfterGameStart { get; set; } = false;
public Update Update { get; set; } = new Update();
public Launch Launch { get; set; } = new Launch();

diff:
<StackPanel Grid.Column="1" Margin="5,0,0,0">
<TextBlock TextWrapping="Wrap" FontSize="16" FontFamily="Roboto, Segoe UI" FontWeight="Medium" Foreground="WhiteSmoke" Text="{Binding Name}" />
<TextBlock TextWrapping="Wrap" FontSize="13" FontFamily="Roboto, Segoe UI" Foreground="WhiteSmoke" Text="{Binding Description}"/>
- <TextBlock FontSize="12" VerticalAlignment="Bottom" FontFamily="Roboto, Segoe UI" FontWeight="Medium" Foreground="Gray" Margin="0,0,5,0" Text="{Binding SubText}" />
+ <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom">
+ <Image Source="{StaticResource ICON_DOWNLOAD_GRAY}" Width="10" Height="10" Margin="0,1,2,0" />
+ <TextBlock FontSize="12" FontFamily="Roboto, Segoe UI" FontWeight="Medium" Foreground="Gray"
+ Text="{Binding SubText}"/>
+ </StackPanel>
</StackPanel>
<!-- buttons -->

diff:
@@ -37,11 +37,19 @@ namespace HunterPie.Plugins
return JsonConvert.DeserializeObject<List<PluginRegistryEntry>>(str);
}
+ public async Task<int> ReportInstall(string pluginName)
+ {
+ var url = BuildUrl(UserSettings.PlayerConfig.HunterPie.PluginRegistryUrl, "plugin", Uri.EscapeUriString(pluginName), "install");
+ var rsp = await client.PostAsync(url, new StringContent(""));
+ rsp.EnsureSuccessStatusCode();
+ return int.Parse(await rsp.Content.ReadAsStringAsync());
+ }
+
public async Task<string> GetModuleUpdateUrl(PluginInformation pInformation)
{
if (await ShouldUseProxy())
{
- return BuildUrl(UserSettings.PlayerConfig.HunterPie.PluginRegistryUrl, $"plugin/{pInformation.Name}/module");
+ return BuildUrl(UserSettings.PlayerConfig.HunterPie.PluginRegistryUrl, "plugin", Uri.EscapeUriString(pInformation.Name), "module");
}
return pInformation.Update.UpdateUrl;
@@ -108,5 +116,7 @@ namespace HunterPie.Plugins
public string Author { get; set; }
public string Version { get; set; }
+
+ public int Downloads { get; set; }
}
}

diff:
@@ -38,6 +38,8 @@ namespace HunterPie.Plugins
private void OnRegistryPluginsLoaded(object sender, EventArgs e)
{
GetImage();
+ // handle downloads count change
+ OnPropertyChanged(nameof(SubText));
}
public void Delete()
@@ -131,7 +133,19 @@ namespace HunterPie.Plugins
public string Description => plugin.PluginInformation.Description;
public string SubText => GStrings.GetLocalizationByXPath("/Console/String[@ID='MESSAGE_PLUGIN_SUBTITLE_ROW']")
.Replace("{version}", plugin.PluginInformation.Version)
- .Replace("{author}", plugin.PluginInformation.Author);
+ .Replace("{author}", plugin.PluginInformation.Author)
+ .Replace("{downloads}", GetDownloadsCount());
+
+ private string GetDownloadsCount()
+ {
+ var val = pluginList.TryGetRegistryEntry(plugin.PluginInformation.Name)?.Downloads;
+ if (val == null)
+ {
+ return "-";
+ }
+
+ return val.ToString();
+ }
public bool IsEnabled
{

diff:
@@ -59,6 +59,8 @@ namespace HunterPie.Plugins
var moduleContent = await http.GetStringAsync(entry.Module);
var modulePath = await Hunterpie.Instance.InstallPlugin(moduleContent);
+ // we don't actually care if this request is failed, nor interested in value, so we will not await it
+ _ = PluginRegistryService.Instance.ReportInstall(entry.InternalName);
DownloadReadme(modulePath);
pluginList.UpdatePluginsArrangement();
@@ -130,7 +132,7 @@ namespace HunterPie.Plugins
public bool IsVersionOk => PluginUpdate.IsVersionOk(entry.MinVersion);
public bool CanToggle => false;
- public bool CanDelete => !IsBusy && PluginManager.IsInstalled(entry.InternalName);
+ public bool CanDelete => !IsBusy && PluginManager.IsInstalled(entry.InternalName) && !PluginManager.IsMarkedForRemoval(entry.InternalName);
public bool CanInstall => !IsBusy && !PluginManager.IsInstalled(entry.InternalName);
public bool CanRestore => false;
public bool IsFailed => false;
@@ -146,7 +148,8 @@ namespace HunterPie.Plugins
public string Description => entry.Description;
public string SubText => GStrings.GetLocalizationByXPath("/Console/String[@ID='MESSAGE_PLUGIN_SUBTITLE_ROW']")
.Replace("{version}", entry.Version)
- .Replace("{author}", entry.Author);
+ .Replace("{author}", entry.Author)
+ .Replace("{downloads}", entry.Downloads.ToString());
public bool IsEnabled
{

diff:
<String ID="MESSAGE_FRESH_PLUGINS" Name="Will be enabled after restart"/>
<String ID="MESSAGE_AVAILABLE_PLUGINS" Name="Available Plugins"/>
<String ID="MESSAGE_TO_DELETE_PLUGINS" Name="Will be deleted after restart"/>
- <String ID="MESSAGE_PLUGIN_SUBTITLE_ROW" Name="ver: {version}; author: {author}"/>
+ <String ID="MESSAGE_PLUGIN_SUBTITLE_ROW" Name="{downloads} | v{version} | by {author}"/>
<String ID="MESSAGE_PLUGIN_INCOMPATIBLE_VERSION" Name="HunterPie version is incompatible with this plugin."/>
<String ID="MESSAGE_REFRESH" Name="Refresh" />
<String ID="MESSAGE_PLUGIN_DELETE" Name="Delete plugin" />

diff:
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
+ <DrawingImage x:Key="ICON_DOWNLOAD_GRAY">
+ <DrawingImage.Drawing>
+ <DrawingGroup>
+ <GeometryDrawing Brush="Gray" Geometry="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z" />
+ </DrawingGroup>
+ </DrawingImage.Drawing>
+ </DrawingImage>
<DrawingImage x:Key="ICON_REMOVE">
<DrawingImage.Drawing>
<DrawingGroup>


message:
Plugins activation/deactivation in separate thread. Also:
fix typo
extended gitignore for Nuget packages

old_path: .gitignore

new_path: .gitignore

diff:
.vs
bin
obj
+packages
# Ignore local server files
docs/server.js

diff:
<String ID="MESSAGE_REFRESH" Name="Refresh" />
<String ID="MESSAGE_PLUGIN_DELETE" Name="Delete plugin" />
<String ID="MESSAGE_PLUGIN_RESTORE" Name="Restore plugin" />
- <String ID="MESSAGE_PLUGIN_FAILED" Name="Plugin initialization failed. It will not be loaded util HunterPie restart." />
+ <String ID="MESSAGE_PLUGIN_FAILED" Name="Plugin initialization failed. It will not be loaded until HunterPie restart." />
<String ID="MESSAGE_README_EMPTY" Name="Empty..." />
<!-- Popups -->
<String ID="MESSAGE_QUIT" Name="Are you sure you want to exit HunterPie?"/>

original_message:
Plugins activation/deactivation in separate thread. Also:
- fix typo
- extended gitignore for Nuget packages


message: caret brush fix

diff:
<Grid.Resources>
- <Style TargetType="TextBox" BasedOn="{StaticResource TextBoxStyle}">
+ <Style TargetType="TextBox">
+ <Setter Property="CaretBrush" Value="{StaticResource BUTTON_CLOSEWINDOW_BACKGROUND}"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSearchBarActive, ElementName=SearchBarControl}" Value="False">
<Setter Property="IsEnabled" Value="False" />

diff:
</Setter>
</Style>
- <Style TargetType="{x:Type TextBox}" x:Key="TextBoxStyle">
+ <Style TargetType="{x:Type TextBox}">
<Setter Property="CaretBrush" Value="{StaticResource BUTTON_CLOSEWINDOW_BACKGROUND}"/>
</Style>

original_message: caret brush fix


message: conditional language embedding

diff:
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
+ <Choose>
+ <When Condition=" '$(Configuration)'!='Release'">
<ItemGroup>
<EmbeddedResource Include="Languages\en-us.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
+ </When>
+ <Otherwise>
+ <ItemGroup>
+ <None Include="Languages\en-us.xml">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </None>
+ </ItemGroup>
+ </Otherwise>
+ </Choose>
<ItemGroup>
<None Include="Languages\es-es.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>

original_message: conditional language embedding


message: implemented zoom for plugin readme

diff:
mc:Ignorable="d"
x:Name="PluginsControl"
Margin="-1,0,0,0"
- d:DesignHeight="450" d:DesignWidth="800" DataContext="{StaticResource Localization}">
+ d:DesignHeight="450" d:DesignWidth="800" DataContext="{Binding RelativeSource={RelativeSource Self}}">
+ <Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<guiControls:PluginListControl Grid.Row="1" DataContext="{Binding ElementName=PluginsControl, Path=PluginList}"/>
</Grid>
+
+ <!-- image preview -->
+ <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="#88000000">
+ <Grid.InputBindings>
+ <MouseBinding MouseAction="LeftClick" Command="{Binding Path=CloseImagePreviewCommand}"/>
+ <MouseBinding MouseAction="RightClick" Command="{Binding Path=CloseImagePreviewCommand}"/>
+ </Grid.InputBindings>
+
+ <Grid.Style>
+ <Style TargetType="Grid">
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding IsPreviewVisible}" Value="True">
+ <DataTrigger.EnterActions>
+ <BeginStoryboard>
+ <Storyboard>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
+ Duration="0">
+ <DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" />
+ </ObjectAnimationUsingKeyFrames>
+ <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:.2" />
+ </Storyboard>
+ </BeginStoryboard>
+ </DataTrigger.EnterActions>
+ <DataTrigger.ExitActions>
+ <BeginStoryboard>
+ <Storyboard>
+ <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:.2" />
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
+ Duration="0"
+ BeginTime="0:0:.2">
+ <DiscreteObjectKeyFrame Value="{x:Static Visibility.Hidden}" />
+ </ObjectAnimationUsingKeyFrames>
+ </Storyboard>
+ </BeginStoryboard>
+ </DataTrigger.ExitActions>
+ </DataTrigger>
+ </Style.Triggers>
+ <Setter Property="Visibility" Value="Hidden" />
+ </Style>
+ </Grid.Style>
+
+ <Image Source="{Binding PreviewImg}" Margin="5" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Stretch="Uniform" />
+ </Grid>
+ </Grid>
</UserControl>

diff:
@@ -4,6 +4,7 @@ using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
+using System.Windows.Media;
using HunterPie.Plugins;
using HunterPie.UI.Infrastructure;
@@ -28,6 +29,29 @@ namespace HunterPie.GUIControls
}
}
+ public static readonly DependencyProperty PreviewImgProperty = DependencyProperty.Register(
+ "PreviewImg", typeof(ImageSource), typeof(Plugins), new PropertyMetadata(default(ImageSource)));
+
+ public ImageSource PreviewImg
+ {
+ get { return (ImageSource)GetValue(PreviewImgProperty); }
+ set
+ {
+ SetValue(PreviewImgProperty, value);
+ IsPreviewVisible = value != null;
+ CommandManager.InvalidateRequerySuggested();
+ }
+ }
+
+ public static readonly DependencyProperty IsPreviewVisibleProperty = DependencyProperty.Register(
+ "IsPreviewVisible", typeof(bool), typeof(Plugins), new PropertyMetadata(default(bool)));
+
+ public bool IsPreviewVisible
+ {
+ get { return (bool)GetValue(IsPreviewVisibleProperty); }
+ set { SetValue(IsPreviewVisibleProperty, value); }
+ }
+
public PluginListViewModel PluginList { get; set; }
public ICommand OpenPluginsFolderCommand { get; } = new RelayCommand(
@@ -40,35 +64,53 @@ namespace HunterPie.GUIControls
_ => Hunterpie.Instance.Reload()
);
- public static readonly DependencyProperty SearchOpenedProperty = DependencyProperty.Register(
- "SearchOpened", typeof(bool), typeof(Plugins), new PropertyMetadata(default(bool)));
- private KeyBinding SearchBinding { get; }
- public KeyBinding RefreshBinding { get; }
+ public ICommand MagnifyImageCommand { get; }
+
+ public ICommand CloseImagePreviewCommand { get; }
+
+
+ private KeyBinding[] KeyBindings { get; }
public Plugins()
{
PluginList = new PluginListViewModel(PluginRegistryService.Instance);
+
+ // commands
+ MagnifyImageCommand = new RelayCommand(Magnify);
+ CloseImagePreviewCommand = new ArglessRelayCommand(ClearMagnify);
+
InitializeComponent();
- SearchBinding = new KeyBinding(SearchBarControl.ToggleSearchCommand, Key.F, ModifierKeys.Control)
+ KeyBindings = CreateKeyBindings();
+ PluginList.Refresh();
+ }
+
+ private KeyBinding[] CreateKeyBindings() => new[]
+ {
+ new KeyBinding(SearchBarControl.ToggleSearchCommand, Key.F, ModifierKeys.Control)
{
CommandParameter = true
+ },
+ new KeyBinding(PluginList.RefreshCommand, Key.F5, ModifierKeys.None),
+ new KeyBinding(CloseImagePreviewCommand, Key.Escape, ModifierKeys.None)
};
- RefreshBinding = new KeyBinding(PluginList.RefreshCommand, Key.F5, ModifierKeys.None);
- PluginList.Refresh();
- }
public void OnActivate()
{
- Hunterpie.Instance.InputBindings.Add(SearchBinding);
- Hunterpie.Instance.InputBindings.Add(RefreshBinding);
+ Hunterpie.Instance.InputBindings.AddRange(KeyBindings);
}
public void OnDeactivate()
{
- Hunterpie.Instance.InputBindings.Remove(SearchBinding);
- Hunterpie.Instance.InputBindings.Remove(RefreshBinding);
+ foreach (var binding in KeyBindings)
+ {
+ Hunterpie.Instance.InputBindings.Remove(binding);
}
}
+
+ public void Magnify(object arg) => PreviewImg = arg as ImageSource;
+
+ public void ClearMagnify() => PreviewImg = null;
+ }
}

diff: Binary files /dev/null and b/HunterPie/HunterPie.Resources/UI/magnifying-glass-white.cur differ

diff:
<Content Include="HunterPie.Resources\UI\damage_plot.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <None Include="HunterPie.Resources\UI\magnifying-glass-white.cur">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

diff:
<!-- Console content -->
<Grid x:Name="ConsolePanel" Margin="51,157,0,2"/>
<Rectangle Height="130" VerticalAlignment="Top" Margin="0,22,0,0" Fill="{StaticResource CONSOLE_HEADER_BACKGROUND}"/>
- <Rectangle Cursor="SizeAll" x:Name="WindowTopBar" Fill="{StaticResource CONSOLE_WINDOWBAR_BACKGROUND}" Height="22" VerticalAlignment="Top" MouseLeftButtonDown="OnWindowDrag"/>
+ <Rectangle Cursor="SizeAll" x:Name="WindowTopBar" Fill="{StaticResource CONSOLE_WINDOWBAR_BACKGROUND}" Height="22" VerticalAlignment="Top" MouseLeftButtonDown="OnWindowDrag">
+ <Rectangle.InputBindings>
+ <MouseBinding Gesture="LeftDoubleClick" Command="{Binding ElementName=window, Path=MaximizeCommand}"/>
+ </Rectangle.InputBindings>
+ </Rectangle>
<Image Height="20" Width="20" Visibility="{Binding ElementName=window, Path=AdministratorIconVisibility}" ToolTip="HunterPie (Administrator)" Source="{StaticResource ICON_ADMINISTRATOR}" VerticalAlignment="Top" HorizontalAlignment="Left" Opacity="0.8" Margin="1,1,0,0" />
<!-- Top buttons -->

diff:
@@ -104,6 +104,8 @@ namespace HunterPie
public static readonly DependencyProperty IsDraggingProperty =
DependencyProperty.Register("IsDragging", typeof(bool), typeof(Hunterpie));
+ public ICommand MaximizeCommand { get; }
+
#endregion
public Hunterpie()
@@ -118,6 +120,8 @@ namespace HunterPie
}
CheckIfHunterPieOpen();
+ MaximizeCommand = new ArglessRelayCommand(() => WindowState = WindowState.Maximized);
+
AppDomain.CurrentDomain.UnhandledException += ExceptionLogger;
IsPlayerLoggedOn = false;

diff:
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
+ <Cursor x:Key="CURSOR_MAGNIFY">pack://siteoforigin:,,,/HunterPie.Resources/UI/magnifying-glass-white.cur</Cursor>
<!-- Styles -->
<Style x:Key="WidgetStyle" TargetType="{x:Type Window}">
<Style.Triggers>

original_message: implemented zoom for plugin readme


message: custom controls bindings fix

diff:
TextChanged="TextBoxBase_OnTextChanged"
LostFocus="TextBox_OnLostFocus"
/>
- <Slider Value="{Binding Value, ElementName=SliderControl, Mode=TwoWay}"
+ <Slider Value="{Binding Value, ElementName=SliderControl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Maximum="{Binding Path=MaxValue, ElementName=SliderControl, Mode=OneWay}"
Minimum="{Binding Path=MinValue, ElementName=SliderControl, Mode=OneWay}"
TickFrequency="{Binding Path=MinChange, ElementName=SliderControl, Mode=OneWay}"

diff:
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
x:Name="TextInputCtrl"
mc:Ignorable="d"
- DataContext="{Binding RelativeSource={RelativeSource Self}}"
Height="30" Margin="10,0,10,5">
<DockPanel>
- <TextBlock Text="{Binding Label}" Foreground="WhiteSmoke" DockPanel.Dock="Left" FontFamily="Segoe UI Light" Padding="0,0,10,0"
+ <TextBlock Text="{Binding Label, ElementName=TextInputCtrl}" Foreground="WhiteSmoke" DockPanel.Dock="Left" FontFamily="Segoe UI Light" Padding="0,0,10,0"
VerticalAlignment="Center"/>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" DockPanel.Dock="Right">
- <TextBox x:Name="TextBox" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"
+ <TextBox x:Name="TextBox" Text="{Binding Text, ElementName=TextInputCtrl, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Background="{x:Null}" Foreground="#ffbfbfbf" BorderBrush="#FFABADB3"
IsReadOnly="{Binding IsReadOnly, ElementName=TextInputCtrl}"
BorderThickness="0,0,0,1" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" />
- <TextBox x:Name="WatermarkTextBox" IsHitTestVisible="False" IsReadOnly="True" Foreground="#cc9E9D9D" Text="{Binding Watermark}"
+ <TextBox x:Name="WatermarkTextBox" IsHitTestVisible="False" IsReadOnly="True" Foreground="#cc9E9D9D" Text="{Binding Watermark, ElementName=TextInputCtr}"
Background="{x:Null}"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"

original_message: custom controls bindings fix


message: add notifications widget

diff:
<Compile Include="Native\Connection\Packets\Definitions.cs" />
<Compile Include="Native\Connection\Packets\Opcodes.cs" />
<Compile Include="Native\Injector.cs" />
+ <Compile Include="Notifications\INotificationsService.cs" />
+ <Compile Include="Notifications\NotificationModel.cs" />
+ <Compile Include="Notifications\NotificationService.cs" />
+ <Compile Include="Notifications\NotificationsServiceExtensions.cs" />
<Compile Include="Plugins\IPlugin.cs" />
<Compile Include="Plugins\PluginInformation.cs" />
<Compile Include="Plugins\PluginPackage.cs" />

diff:
@@ -6,8 +6,8 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
-using System.Windows.Threading;
using HunterPie.Core;
+using HunterPie.GUI.Widgets;
using HunterPie.Logger;
using HunterPie.Memory;
@@ -163,6 +163,12 @@ namespace HunterPie.GUI
counter++;
}
+ if (OverlaySettings.NotificationsWidget.Initialize)
+ {
+ RegisterWidget(new NotificationsWidget());
+ counter++;
+ }
+
Debugger.Warn($"Initialized overlay with {counter} widgets!");
} else
{

diff:
</StackPanel>
</ScrollViewer>
</TabItem>
+ <TabItem>
+ <TabItem.Header>
+ <Canvas>
+ <Image Source="{StaticResource ICON_NOTIFICATIONS}" Width="15" Height="15" Canvas.Top="6" Canvas.Left="2"/>
+ <TextBlock Height="21" Width="181" Canvas.Left="20" FontWeight="Medium" Canvas.Top="2" Text="{strings:Settings NOTIFICATIONS_TEXT}" />
+ </Canvas>
+ </TabItem.Header>
+ <ScrollViewer>
+ <StackPanel>
+ <custom:Switcher x:Name="switchNotificationsInitialize" Text="{strings:Settings ENABLE_INITIALIZE_NOTIFICATIONS}" RestartVisibility="Visible" ToolTip="{strings:Settings ENABLE_INITIALIZE_NOTIFICATIONS_DESC}"/>
+ <custom:Switcher x:Name="switchEnableNotifications" Text="{strings:Settings ENABLE_NOTIFICATIONS}" Margin="10,0,10,5" ToolTip="{strings:Settings ENABLE_NOTIFICATIONS_DESC}" RestartVisibility="Collapsed"/>
+ <custom:MinimalSlider x:Name="NotificationsWidgetOpacity" Text="{strings:Settings STATIC_OPACITY}" MinChange="0.01" MinValue="0" MaxValue="1" ValueText="{Binding Value, RelativeSource={RelativeSource Self}, StringFormat=\{0:P\}}" Foreground="WhiteSmoke" FontFamily="Segoe UI Light" Margin="10,0,10,5" />
+ <custom:Switcher Margin="10,0,10,5" x:Name="switchNotificationStreamerMode" Text="{strings:Settings ENABLE_STREAMER_MODE}" ToolTip="{strings:Settings ENABLE_STREAMER_MODE_DESC}" RestartVisibility="Visible"/>
+ </StackPanel>
+ </ScrollViewer>
+ </TabItem>
+
<TabItem>
<TabItem.Header>
<Canvas>

diff:
<String ID="ENABLE_LIGHTBOWGUN_HELPER" Name="Enable Light Bowgun Helper"/>
<String ID="ENABLE_LIGHTBOWGUN_HELPER_DESC" Name="If enabled, the Light Bowgun helper will be visible whenever you equip a Light Bowgun."/>
<String ID="STATIC_CLASSES_BETA_WARNING" Name="Attention: Class helpers are still in development and more classes will be supported in the future. Each class helper has it's own settings, so you can have different positions for this widget depending on the class. Use the Design Mode feature to scale and move this widget."/>
+
+ <!-- Notifications Widget -->
+ <String ID="NOTIFICATIONS_TEXT" Name="Notifications"/>
+
+ <String ID="ENABLE_NOTIFICATIONS" Name="Enable Notifications Widget"/>
+ <String ID="ENABLE_NOTIFICATIONS_DESC" Name="If enabled notifications will be shown"/>
+
+ <String ID="ENABLE_INITIALIZE_NOTIFICATIONS" Name="Initialize Notifications Widget"/>
+ <String ID="ENABLE_INITIALIZE_NOTIFICATIONS_DESC" Name="If disabled, the notifications widget won't be initialized at all."/>
+
<!-- Links -->
<String ID="STATIC_HUNTERPIE_WIKI" Name="Documentation"/>
<String ID="STATIC_HUNTERPIE_WIKI_DESC" Name="HunterPie official documentation."/>

diff:
xmlns:Overlay="clr-namespace:HunterPie.GUI;assembly=HunterPie.UI"
xmlns:PlayerHud="clr-namespace:HunterPie.GUI.Widgets.HealthWidget;assembly=HunterPie.UI"
xmlns:PlayerHealthParts="clr-namespace:HunterPie.GUI.Widgets.HealthWidget.Parts;assembly=HunterPie.UI"
- >
+ xmlns:notifications="clr-namespace:HunterPie.GUI.Widgets.Notifications;assembly=HunterPie.UI">
<SolidColorBrush x:Key="NoteColor0" Color="#FF724CC5"/>
<SolidColorBrush x:Key="NoteColor1" Color="#FFDC2F35"/>
<SolidColorBrush x:Key="NoteColor2" Color="#FFFF8000"/>
</Style.Triggers>
</Style>
+ <!--Notifications-->
+ <Color x:Key="NOTIFICATION_BKG_COLOR_1">#ff087cac</Color>
+ <Color x:Key="NOTIFICATION_BKG_COLOR_2">#00087cac</Color>
+ <Color x:Key="NOTIFICATION_HEADER_COLOR">#ee9833</Color>
+ <Color x:Key="NOTIFICATION_TEXT_COLOR">#ffffff</Color>
+ <sys:Int32 x:Key="NOTIFICATION_HIDE_DELAY">300</sys:Int32>
+
+ <SolidColorBrush x:Key="NOTIFICATION_BKG_BRUSH_1" Color="{StaticResource NOTIFICATION_BKG_COLOR_1}" />
+ <SolidColorBrush x:Key="NOTIFICATION_HEADER_BRUSH" Color="{StaticResource NOTIFICATION_HEADER_COLOR}" />
+ <SolidColorBrush x:Key="NOTIFICATION_TEXT_BRUSH" Color="{StaticResource NOTIFICATION_TEXT_COLOR}" />
+
+ <LinearGradientBrush x:Key="NOTIFICATION_BKG" StartPoint="0,0" EndPoint="1,1">
+ <GradientStop Color="{StaticResource NOTIFICATION_BKG_COLOR_2}" Offset="0" />
+ <GradientStop Color="{StaticResource NOTIFICATION_BKG_COLOR_1}" Offset="0.5"/>
+ <GradientStop Color="{StaticResource NOTIFICATION_BKG_COLOR_2}" Offset="1"/>
+ </LinearGradientBrush>
+
+ <LinearGradientBrush x:Key="NOTIFICATION_BORDER_BKG" StartPoint="0,0" EndPoint="1,1">
+ <GradientStop Color="{StaticResource NOTIFICATION_BKG_COLOR_1}" Offset="0.6" />
+ <GradientStop Color="{StaticResource NOTIFICATION_BKG_COLOR_2}" Offset="1"/>
+ </LinearGradientBrush>
+
+ <ControlTemplate x:Key="NOTIFICATION_BKG_TEMPLATE">
+ <Grid>
+ <Rectangle Fill="{StaticResource NOTIFICATION_BKG}" Margin="0,6"/>
+
+ <Line Stroke="{StaticResource NOTIFICATION_BKG_BRUSH_1}" X1="0" Y2="1" X2="11" Y1="10" StrokeThickness="3" />
+ <Line Stroke="{StaticResource NOTIFICATION_BKG_BRUSH_1}" X1="0" Y1="1" X2="11" Y2="10" StrokeThickness="3" VerticalAlignment="Bottom" />
+
+ <Grid Margin="10,0,0,0">
+ <Rectangle Height="3" VerticalAlignment="Top" Fill="{StaticResource NOTIFICATION_BORDER_BKG}"/>
+ <Rectangle Height="3" VerticalAlignment="Bottom" Fill="{StaticResource NOTIFICATION_BORDER_BKG}"/>
+ </Grid>
+ </Grid>
+ </ControlTemplate>
+
+ <Style TargetType="Grid" x:Key="NOTIFICATION_CONTROL_STYLE">
+ <Setter Property="Margin" Value="0, 10,0,0"/>
+ </Style>
+
+ <Style TargetType="Grid" x:Key="NOTIFICATION_BODY_STYLE">
+ <Setter Property="Margin" Value="15, 10,15,8"/>
+ </Style>
+
+ <Style TargetType="TextBlock" x:Key="NOTIFICATION_BASE_TEXTBLOCK_STYLE">
+ <Setter Property="Effect">
+ <Setter.Value>
+ <DropShadowEffect BlurRadius="7" Color="Black" RenderingBias="Performance" Opacity="1" Direction="-30" ShadowDepth="4" />
+ </Setter.Value>
+ </Setter>
+
+ <Setter Property="FontSize" Value="20"/>
+ <Setter Property="FontFamily" Value="Roboto"/>
+ <Setter Property="FontWeight" Value="Bold"/>
+ <Setter Property="VerticalAlignment" Value="Center"/>
+ </Style>
+
+ <Style TargetType="TextBlock" x:Key="NOTIFICATION_HEADER_TEXTBLOCK_STYLE" BasedOn="{StaticResource NOTIFICATION_BASE_TEXTBLOCK_STYLE}">
+ <Setter Property="Foreground" Value="{StaticResource NOTIFICATION_HEADER_BRUSH}"/>
+ </Style>
+
+ <Style TargetType="TextBlock" x:Key="NOTIFICATION_TEXT_TEXTBLOCK_STYLE" BasedOn="{StaticResource NOTIFICATION_BASE_TEXTBLOCK_STYLE}">
+ <Setter Property="Foreground" Value="{StaticResource NOTIFICATION_TEXT_BRUSH}"/>
+ </Style>
+
+ <Style TargetType="UserControl" x:Key="NOTIFICATION_STYLE">
+ <Setter Property="Margin" Value="0,10,0,0"/>
+
+ <Style.Triggers>
+ <Trigger Property="notifications:Notification.ShouldBeShown" Value="True">
+ <Trigger.EnterActions>
+ <BeginStoryboard>
+ <Storyboard>
+ <DoubleAnimation Storyboard.TargetProperty="(UserControl.RenderTransform).(TranslateTransform.X)" From="300" To="0" Duration="0:0:0.2">
+ <DoubleAnimation.EasingFunction>
+ <PowerEase EasingMode="EaseOut" />
+ </DoubleAnimation.EasingFunction>
+ </DoubleAnimation>
+ <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.2"/>
+ </Storyboard>
+ </BeginStoryboard>
+ </Trigger.EnterActions>
+
+ <Trigger.ExitActions>
+ <BeginStoryboard>
+ <Storyboard>
+ <DoubleAnimation Storyboard.TargetProperty="(UserControl.RenderTransform).(TranslateTransform.X)" From="0" To="300" Duration="0:0:0.2">
+ <DoubleAnimation.EasingFunction>
+ <PowerEase EasingMode="EaseOut" />
+ </DoubleAnimation.EasingFunction>
+ </DoubleAnimation>
+ <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.2"/>
+ </Storyboard>
+ </BeginStoryboard>
+ </Trigger.ExitActions>
+ </Trigger>
+ </Style.Triggers>
+ </Style>
+
</ResourceDictionary>

original_message: add notifications widget