File: tag.rb

package info (click to toggle)
gitlab 17.6.5-19
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 629,368 kB
  • sloc: ruby: 1,915,304; javascript: 557,307; sql: 60,639; xml: 6,509; sh: 4,567; makefile: 1,239; python: 406
file content (167 lines) | stat: -rw-r--r-- 3,530 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# frozen_string_literal: true

module ContainerRegistry
  class Tag
    include Gitlab::Utils::StrongMemoize

    attr_reader :repository, :name, :updated_at, :referrers, :published_at
    attr_writer :created_at, :manifest_digest, :revision, :total_size
    attr_accessor :media_type

    delegate :registry, :client, to: :repository

    def initialize(repository, name, from_api: false)
      @repository = repository
      @name = name
      @from_api = from_api
    end

    def referrers=(refs)
      @referrers = Array.wrap(refs).map { |ref| Referrer.new(ref['artifactType'], ref['digest'], self) }
    end

    def revision
      @revision || config_blob&.revision
    end

    def short_revision
      return unless revision

      revision[0..8]
    end

    def valid?
      from_api? || manifest.present?
    end

    def latest?
      name == "latest"
    end

    def v1?
      manifest && manifest['schemaVersion'] == 1
    end

    def v2?
      manifest && manifest['schemaVersion'] == 2
    end

    def manifest
      strong_memoize(:manifest) do
        client.repository_manifest(repository.path, name)
      end
    end

    def path
      "#{repository.path}:#{name}"
    end

    def location
      "#{repository.location}:#{name}"
    end

    def [](key)
      return unless manifest

      manifest[key]
    end

    def digest
      strong_memoize(:digest) do
        @manifest_digest || client.repository_tag_digest(repository.path, name)
      end
    end

    def config_blob
      return unless manifest && manifest['config']

      strong_memoize(:config_blob) do
        repository.blob(manifest['config'])
      end
    end

    def config
      return unless config_blob&.data

      strong_memoize(:config) do
        ContainerRegistry::Config.new(self, config_blob)
      end
    end

    def created_at
      return @created_at if @created_at

      strong_memoize(:memoized_created_at) do
        next unless config

        DateTime.rfc3339(config['created'])
      rescue ArgumentError
        nil
      end
    end

    # this function will set and memoize a created_at
    # to avoid a #config_blob call.
    def force_created_at_from_iso8601(string_value)
      date = parse_iso8601_string(string_value)
      instance_variable_set(ivar(:memoized_created_at), date)
    end

    def updated_at=(string_value)
      return unless string_value

      @updated_at = parse_iso8601_string(string_value)
    end

    def published_at=(string_value)
      return unless string_value

      @published_at = parse_iso8601_string(string_value)
    end

    def layers
      return unless manifest

      strong_memoize(:layers) do
        layers = manifest['layers'] || manifest['fsLayers']

        layers.map do |layer|
          repository.blob(layer)
        end
      end
    end

    def put(digests)
      repository.client.put_tag(repository.path, name, digests)
    end

    def total_size
      return @total_size if @total_size

      return unless layers

      layers.sum(&:size) if v2?
    end

    # Deletes the image associated with this tag
    # Note this will delete the image and all tags associated with it.
    # Consider using DeleteTagsService instead.
    def unsafe_delete
      return unless digest

      client.delete_repository_tag_by_digest(repository.path, digest)
    end

    private

    def from_api?
      @from_api
    end

    def parse_iso8601_string(string_value)
      DateTime.iso8601(string_value)
    rescue ArgumentError
      nil
    end
  end
end