diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/diffs/components/diff_file_header.vue | 2 | ||||
-rw-r--r-- | app/assets/javascripts/ide/components/ide_tree.vue | 2 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/datetime_utility.js | 18 | ||||
-rw-r--r-- | app/assets/javascripts/vue_shared/components/timezone_dropdown.vue | 11 | ||||
-rw-r--r-- | app/models/ci/build_trace_chunk.rb | 12 | ||||
-rw-r--r-- | app/models/ci/build_trace_chunks/fog.rb | 18 | ||||
-rw-r--r-- | app/models/ci/build_trace_chunks/legacy_fog.rb | 77 |
7 files changed, 126 insertions, 14 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue index b08b9df13a4..9f451cd759a 100644 --- a/app/assets/javascripts/diffs/components/diff_file_header.vue +++ b/app/assets/javascripts/diffs/components/diff_file_header.vue @@ -222,7 +222,7 @@ export default { <a ref="titleWrapper" :v-once="!viewDiffsFileByFile" - class="gl-mr-2 gl-text-decoration-none!" + class="gl-mr-2 gl-text-decoration-none! gl-text-truncate" :href="titleLink" @click="handleFileNameClick" > diff --git a/app/assets/javascripts/ide/components/ide_tree.vue b/app/assets/javascripts/ide/components/ide_tree.vue index 51d783df0ad..56fcb6c2600 100644 --- a/app/assets/javascripts/ide/components/ide_tree.vue +++ b/app/assets/javascripts/ide/components/ide_tree.vue @@ -54,7 +54,7 @@ export default { <ide-tree-list> <template #header> {{ __('Edit') }} - <div class="ide-tree-actions ml-auto d-flex"> + <div class="ide-tree-actions ml-auto d-flex" data-testid="ide-root-actions"> <new-entry-button :label="__('New file')" :show-label="false" diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 6e78dc87c02..753245147d2 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -658,6 +658,24 @@ export const secondsToMilliseconds = seconds => seconds * 1000; export const secondsToDays = seconds => Math.round(seconds / 86400); /** + * Converts a numeric utc offset in seconds to +/- hours + * ie -32400 => -9 hours + * ie -12600 => -3.5 hours + * + * @param {Number} offset UTC offset in seconds as a integer + * + * @return {String} the + or - offset in hours + */ +export const secondsToHours = offset => { + const parsed = parseInt(offset, 10); + if (Number.isNaN(parsed) || parsed === 0) { + return `0`; + } + const num = offset / 3600; + return parseInt(num, 10) !== num ? num.toFixed(1) : num; +}; + +/** * Returns the date n days after the date provided * * @param {Date} date the initial date diff --git a/app/assets/javascripts/vue_shared/components/timezone_dropdown.vue b/app/assets/javascripts/vue_shared/components/timezone_dropdown.vue index f6721f5a27b..3fa8efcd145 100644 --- a/app/assets/javascripts/vue_shared/components/timezone_dropdown.vue +++ b/app/assets/javascripts/vue_shared/components/timezone_dropdown.vue @@ -2,6 +2,7 @@ import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui'; import { __ } from '~/locale'; import autofocusonshow from '~/vue_shared/directives/autofocusonshow'; +import { secondsToHours } from '~/lib/utils/datetime_utility'; export default { name: 'TimezoneDropdown', @@ -58,16 +59,8 @@ export default { isSelected(timezone) { return this.value === timezone.formattedTimezone; }, - formatUtcOffset(offset) { - const parsed = parseInt(offset, 10); - if (Number.isNaN(parsed) || parsed === 0) { - return `0`; - } - const prefix = offset > 0 ? '+' : '-'; - return `${prefix}${Math.abs(offset / 3600)}`; - }, formatTimezone(item) { - return `[UTC ${this.formatUtcOffset(item.offset)}] ${item.name}`; + return `[UTC ${secondsToHours(item.offset)}] ${item.name}`; }, }, }; diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb index 6926ccd9438..cf6eb159f52 100644 --- a/app/models/ci/build_trace_chunk.rb +++ b/app/models/ci/build_trace_chunk.rb @@ -45,6 +45,10 @@ module Ci def get_store_class(store) @stores ||= {} + + # Can't memoize this because the feature flag may alter this + return fog_store_class.new if store.to_sym == :fog + @stores[store] ||= "Ci::BuildTraceChunks::#{store.capitalize}".constantize.new end @@ -74,6 +78,14 @@ module Ci def metadata_attributes attribute_names - %w[raw_data] end + + def fog_store_class + if Feature.enabled?(:ci_trace_new_fog_store, default_enabled: true) + Ci::BuildTraceChunks::Fog + else + Ci::BuildTraceChunks::LegacyFog + end + end end def data diff --git a/app/models/ci/build_trace_chunks/fog.rb b/app/models/ci/build_trace_chunks/fog.rb index b1e9fd1faeb..d3051e3dadc 100644 --- a/app/models/ci/build_trace_chunks/fog.rb +++ b/app/models/ci/build_trace_chunks/fog.rb @@ -8,13 +8,17 @@ module Ci end def data(model) - connection.get_object(bucket_name, key(model))[:body] + files.get(key(model))&.body rescue Excon::Error::NotFound # If the object does not exist in the object storage, this method returns nil. end def set_data(model, new_data) - connection.put_object(bucket_name, key(model), new_data) + # TODO: Support AWS S3 server side encryption + files.create({ + key: key(model), + body: new_data + }) end def append_data(model, new_data, offset) @@ -43,7 +47,7 @@ module Ci def delete_keys(keys) keys.each do |key| - connection.delete_object(bucket_name, key_raw(*key)) + files.destroy(key_raw(*key)) end end @@ -69,6 +73,14 @@ module Ci @connection ||= ::Fog::Storage.new(object_store.connection.to_hash.deep_symbolize_keys) end + def fog_directory + @fog_directory ||= connection.directories.new(key: bucket_name) + end + + def files + @files ||= fog_directory.files + end + def object_store Gitlab.config.artifacts.object_store end diff --git a/app/models/ci/build_trace_chunks/legacy_fog.rb b/app/models/ci/build_trace_chunks/legacy_fog.rb new file mode 100644 index 00000000000..b710ed2890b --- /dev/null +++ b/app/models/ci/build_trace_chunks/legacy_fog.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module Ci + module BuildTraceChunks + class LegacyFog + def available? + object_store.enabled + end + + def data(model) + connection.get_object(bucket_name, key(model))[:body] + rescue Excon::Error::NotFound + # If the object does not exist in the object storage, this method returns nil. + end + + def set_data(model, new_data) + connection.put_object(bucket_name, key(model), new_data) + end + + def append_data(model, new_data, offset) + if offset > 0 + truncated_data = data(model).to_s.byteslice(0, offset) + new_data = truncated_data + new_data + end + + set_data(model, new_data) + new_data.bytesize + end + + def size(model) + data(model).to_s.bytesize + end + + def delete_data(model) + delete_keys([[model.build_id, model.chunk_index]]) + end + + def keys(relation) + return [] unless available? + + relation.pluck(:build_id, :chunk_index) + end + + def delete_keys(keys) + keys.each do |key| + connection.delete_object(bucket_name, key_raw(*key)) + end + end + + private + + def key(model) + key_raw(model.build_id, model.chunk_index) + end + + def key_raw(build_id, chunk_index) + "tmp/builds/#{build_id.to_i}/chunks/#{chunk_index.to_i}.log" + end + + def bucket_name + return unless available? + + object_store.remote_directory + end + + def connection + return unless available? + + @connection ||= ::Fog::Storage.new(object_store.connection.to_hash.deep_symbolize_keys) + end + + def object_store + Gitlab.config.artifacts.object_store + end + end + end +end |