def find_objects_for_index
@objects ||= model_class
- @objects = @objects.filter(@filters).limit(@limit).offset(@offset)
+ @objects = @objects.filter(@filters).limit(@limit).offset(@offset).order(@order)
@objects.fetch_multiple_pages(false)
end
ConfigValidators.validate_wb2_url_config()
ConfigValidators.validate_download_config()
end
+ if Rails.configuration.Users.AnonymousUserToken and
+ !Rails.configuration.Users.AnonymousUserToken.starts_with?("v2/")
+ Rails.configuration.Users.AnonymousUserToken = "v2/#{clusterID}-gj3su-anonymouspublic/#{Rails.configuration.Users.AnonymousUserToken}"
+ end
end
done
"$bundle" version | tee /dev/stderr | grep -q 'version 2'
) || fatal 'install bundler'
+ if test -d /var/lib/arvados-arvbox/ ; then
+ # Inside arvbox, use bundler-installed binstubs. The
+ # system bundler and rail's own bin/bundle refuse to work.
+ # I don't know why.
+ bundle=binstubs/bundle
+ fi
fi
}
h3. Anonymous token changes
-The anonymous token configured in @Users.AnonymousUserToken@ must now be 32 characters or longer. This was already the suggestion in the documentation, now it is enforced. The @script/get_anonymous_user_token.rb@ script that was needed to register the anonymous user token in the database has been removed. Registration of the anonymous token is no longer necessary. If the anonymous token in @config.yml@ is specified as a full V2 token, that will now generate a warning - it should be updated to list just the secret (i.e. the part after the last forward slash).
+The anonymous token configured in @Users.AnonymousUserToken@ must now be 32 characters or longer. This was already the suggestion in the documentation, now it is enforced. The @script/get_anonymous_user_token.rb@ script that was needed to register the anonymous user token in the database has been removed. Registration of the anonymous token is no longer necessary.
h3. Preemptible instance support changes
if !strings.HasPrefix(token, "v2/") {
return fmt.Errorf("%s: unacceptable characters in token (only a-z, A-Z, 0-9 are acceptable)", label)
}
- ldr.Logger.Warnf("%s: token is a full V2 token, should just be a secret (remove everything up to and including the last forward slash)", label)
if !acceptableTokenRe.MatchString(tmp[2]) {
return fmt.Errorf("%s: unacceptable characters in V2 token secret (only a-z, A-Z, 0-9 are acceptable)", label)
}
+ if len(tmp[2]) < acceptableTokenLength {
+ ldr.Logger.Warnf("%s: secret is too short (should be at least %d characters)", label, acceptableTokenLength)
+ }
} else if len(token) < acceptableTokenLength {
if ldr.Logger != nil {
ldr.Logger.Warnf("%s: token is too short (should be at least %d characters)", label, acceptableTokenLength)
return nil, errors.New("no token provided")
}
for _, token := range incoming.Tokens {
- if strings.HasPrefix(token, "v2/"+cluster.ClusterID+"-") && remoteID == cluster.Login.LoginCluster {
- // If we did this, the login cluster
- // would call back to us and then
- // reject our response because the
- // user UUID prefix (i.e., the
- // LoginCluster prefix) won't match
- // the token UUID prefix (i.e., our
- // prefix).
+ if strings.HasPrefix(token, "v2/"+cluster.ClusterID+"-") &&
+ !strings.HasPrefix(token, "v2/"+cluster.ClusterID+"-gj3su-anonymouspublic/") &&
+ remoteID == cluster.Login.LoginCluster {
+ // If we did this, the login cluster would call back to us and then
+ // reject our response because the user UUID prefix (i.e., the
+ // LoginCluster prefix) won't match the token UUID prefix (i.e., our
+ // prefix). The anonymous token is OK to forward, because (unlike other
+ // local tokens for real users) the validation callback will return the
+ // locally issued anonymous user ID instead of a login-cluster user ID.
+ // That anonymous user ID gets mapped to the local anonymous user
+ // automatically on the login cluster.
return nil, httpErrorf(http.StatusUnauthorized, "cannot use a locally issued token to forward a request to our login cluster (%s)", remoteID)
}
salted, err := auth.SaltToken(token, remoteID)
c.Check(coll.PortableDataHash, check.Equals, pdh)
}
+// z3333 should forward the locally-issued anonymous user token to its login
+// cluster z1111. That is no problem because the login cluster controller will
+// map any anonymous user token to its local anonymous user.
+//
+// This needs to work because wb1 has a tendency to slap the local anonymous
+// user token on every request as a reader_token, which gets folded into the
+// request token list controller.
+//
+// Use a z1111 user token and the anonymous token from z3333 passed in as a
+// reader_token to do a request on z3333, asking for the z1111 anonymous user
+// object. The request will be forwarded to the z1111 cluster. The presence of
+// the z3333 anonymous user token should not prohibit the request from being
+// forwarded.
+func (s *IntegrationSuite) TestForwardAnonymousTokenToLoginCluster(c *check.C) {
+ conn1 := s.testClusters["z1111"].Conn()
+ s.testClusters["z3333"].Conn()
+
+ rootctx1, _, _ := s.testClusters["z1111"].RootClients()
+ _, anonac3, _ := s.testClusters["z3333"].AnonymousClients()
+
+ // Make a user connection to z3333 (using a z1111 user, because that's the login cluster)
+ _, userac1, _, _ := s.testClusters["z3333"].UserClients(rootctx1, c, conn1, "user@example.com", true)
+
+ // Get the anonymous user token for z3333
+ var anon3Auth arvados.APIClientAuthorization
+ err := anonac3.RequestAndDecode(&anon3Auth, "GET", "/arvados/v1/api_client_authorizations/current", nil, nil)
+ c.Check(err, check.IsNil)
+
+ var userList arvados.UserList
+ where := make(map[string]string)
+ where["uuid"] = "z1111-tpzed-anonymouspublic"
+ err = userac1.RequestAndDecode(&userList, "GET", "/arvados/v1/users", nil,
+ map[string]interface{}{
+ "reader_tokens": []string{anon3Auth.TokenV2()},
+ "where": where,
+ },
+ )
+ // The local z3333 anonymous token must be allowed to be forwarded to the login cluster
+ c.Check(err, check.IsNil)
+
+ userac1.AuthToken = "v2/z1111-gj3su-asdfasdfasdfasd/this-token-does-not-validate-so-anonymous-token-will-be-used-instead"
+ err = userac1.RequestAndDecode(&userList, "GET", "/arvados/v1/users", nil,
+ map[string]interface{}{
+ "reader_tokens": []string{anon3Auth.TokenV2()},
+ "where": where,
+ },
+ )
+ c.Check(err, check.IsNil)
+}
+
// Get a token from the login cluster (z1111), use it to submit a
// container request on z2222.
func (s *IntegrationSuite) TestCreateContainerRequestWithFedToken(c *check.C) {
pagesize = 1000
kwargs["limit"] = pagesize
kwargs["count"] = 'none'
- kwargs["order"] = ["%s %s" % (order_key, "asc" if ascending else "desc"), "uuid asc"]
+ asc = "asc" if ascending else "desc"
+ kwargs["order"] = ["%s %s" % (order_key, asc), "uuid %s" % asc]
other_filters = kwargs.get("filters", [])
if "select" in kwargs and "uuid" not in kwargs["select"]:
if firstitem[order_key] == lastitem[order_key]:
# Got a page where every item has the same order key.
# Switch to using uuid for paging.
- nextpage = [[order_key, "=", lastitem[order_key]], ["uuid", ">", lastitem["uuid"]]]
+ nextpage = [[order_key, "=", lastitem[order_key]], ["uuid", ">" if ascending else "<", lastitem["uuid"]]]
prev_page_all_same_order_key = True
else:
# Start from the last order key seen, but skip the last
def test_onepage_desc(self):
ks = KeysetTestHelper([[
- {"limit": 1000, "count": "none", "order": ["created_at desc", "uuid asc"], "filters": []},
+ {"limit": 1000, "count": "none", "order": ["created_at desc", "uuid desc"], "filters": []},
{"items": [{"created_at": "2", "uuid": "2"}, {"created_at": "1", "uuid": "1"}]}
], [
- {"limit": 1000, "count": "none", "order": ["created_at desc", "uuid asc"], "filters": [["created_at", "<=", "1"], ["uuid", "!=", "1"]]},
+ {"limit": 1000, "count": "none", "order": ["created_at desc", "uuid desc"], "filters": [["created_at", "<=", "1"], ["uuid", "!=", "1"]]},
{"items": []}
]])
clnt
end
- def self.check_anonymous_user_token token
+ def self.check_anonymous_user_token(token:, remote:)
case token[0..2]
when 'v2/'
_, token_uuid, secret, optional = token.split('/')
secret = token
end
+ # Usually, the secret is salted
+ salted_secret = OpenSSL::HMAC.hexdigest('sha1', Rails.configuration.Users.AnonymousUserToken, remote)
+
+ # The anonymous token could be specified as a full v2 token in the config,
+ # but the config loader strips it down to the secret part.
# The anonymous token content and minimum length is verified in lib/config
- if secret.length >= 0 && secret == Rails.configuration.Users.AnonymousUserToken
+ if secret.length >= 0 && (secret == Rails.configuration.Users.AnonymousUserToken || secret == salted_secret)
return ApiClientAuthorization.new(user: User.find_by_uuid(anonymous_user_uuid),
uuid: Rails.configuration.ClusterID+"-gj3su-anonymouspublic",
- api_token: token,
+ api_token: secret,
api_client: anonymous_user_token_api_client,
scopes: ['GET /'])
else
return nil if token.nil? or token.empty?
remote ||= Rails.configuration.ClusterID
- auth = self.check_anonymous_user_token(token)
+ auth = self.check_anonymous_user_token(token: token, remote: remote)
if !auth.nil?
return auth
end
end
def self.default_orders
- ["#{table_name}.modified_at desc", "#{table_name}.uuid"]
+ ["#{table_name}.modified_at desc", "#{table_name}.uuid desc"]
end
def self.unique_columns
--- /dev/null
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+class FixCreatedAtIndexes < ActiveRecord::Migration[5.2]
+ @@idxtables = [:collections, :container_requests, :groups, :links, :repositories, :users, :virtual_machines, :workflows, :logs]
+
+ def up
+ @@idxtables.each do |table|
+ ActiveRecord::Base.connection.execute("DROP INDEX IF EXISTS index_#{table.to_s}_on_created_at")
+ ActiveRecord::Base.connection.execute("DROP INDEX IF EXISTS index_#{table.to_s}_on_created_at_uuid")
+ ActiveRecord::Base.connection.execute("DROP INDEX IF EXISTS index_#{table.to_s}_on_created_at_and_uuid")
+ ActiveRecord::Base.connection.execute("DROP INDEX IF EXISTS index_#{table.to_s}_on_modified_at")
+ ActiveRecord::Base.connection.execute("DROP INDEX IF EXISTS index_#{table.to_s}_on_modified_at_uuid")
+ ActiveRecord::Base.connection.execute("DROP INDEX IF EXISTS index_#{table.to_s}_on_modified_at_and_uuid")
+
+ ActiveRecord::Base.connection.execute("CREATE INDEX IF NOT EXISTS index_#{table.to_s}_on_created_at_and_uuid ON #{table.to_s} USING btree (created_at, uuid)")
+ ActiveRecord::Base.connection.execute("CREATE INDEX IF NOT EXISTS index_#{table.to_s}_on_modified_at_and_uuid ON #{table.to_s} USING btree (modified_at, uuid)")
+ end
+ end
+
+ def down
+ @@idxtables.each do |table|
+ ActiveRecord::Base.connection.execute("DROP INDEX IF EXISTS index_#{table.to_s}_on_modified_at_and_uuid")
+ ActiveRecord::Base.connection.execute("CREATE INDEX IF NOT EXISTS index_#{table.to_s}_on_modified_at_uuid ON #{table.to_s} USING btree (modified_at desc, uuid asc)")
+ end
+ end
+end
--
--- Name: index_collections_on_created_at; Type: INDEX; Schema: public; Owner: -
+-- Name: index_collections_on_created_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_collections_on_created_at ON public.collections USING btree (created_at);
+CREATE INDEX index_collections_on_created_at_and_uuid ON public.collections USING btree (created_at, uuid);
--
--
--- Name: index_collections_on_modified_at; Type: INDEX; Schema: public; Owner: -
+-- Name: index_collections_on_modified_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_collections_on_modified_at ON public.collections USING btree (modified_at);
-
-
---
--- Name: index_collections_on_modified_at_uuid; Type: INDEX; Schema: public; Owner: -
---
-
-CREATE INDEX index_collections_on_modified_at_uuid ON public.collections USING btree (modified_at DESC, uuid);
+CREATE INDEX index_collections_on_modified_at_and_uuid ON public.collections USING btree (modified_at, uuid);
--
--
--- Name: index_container_requests_on_modified_at_uuid; Type: INDEX; Schema: public; Owner: -
+-- Name: index_container_requests_on_created_at_and_uuid; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_container_requests_on_created_at_and_uuid ON public.container_requests USING btree (created_at, uuid);
+
+
+--
+-- Name: index_container_requests_on_modified_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_container_requests_on_modified_at_uuid ON public.container_requests USING btree (modified_at DESC, uuid);
+CREATE INDEX index_container_requests_on_modified_at_and_uuid ON public.container_requests USING btree (modified_at, uuid);
--
--
--- Name: index_groups_on_created_at; Type: INDEX; Schema: public; Owner: -
+-- Name: index_groups_on_created_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_groups_on_created_at ON public.groups USING btree (created_at);
+CREATE INDEX index_groups_on_created_at_and_uuid ON public.groups USING btree (created_at, uuid);
--
--
--- Name: index_groups_on_modified_at; Type: INDEX; Schema: public; Owner: -
+-- Name: index_groups_on_modified_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_groups_on_modified_at ON public.groups USING btree (modified_at);
-
-
---
--- Name: index_groups_on_modified_at_uuid; Type: INDEX; Schema: public; Owner: -
---
-
-CREATE INDEX index_groups_on_modified_at_uuid ON public.groups USING btree (modified_at DESC, uuid);
+CREATE INDEX index_groups_on_modified_at_and_uuid ON public.groups USING btree (modified_at, uuid);
--
--
--- Name: index_links_on_created_at; Type: INDEX; Schema: public; Owner: -
+-- Name: index_links_on_created_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_links_on_created_at ON public.links USING btree (created_at);
+CREATE INDEX index_links_on_created_at_and_uuid ON public.links USING btree (created_at, uuid);
--
--
--- Name: index_links_on_modified_at; Type: INDEX; Schema: public; Owner: -
+-- Name: index_links_on_modified_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_links_on_modified_at ON public.links USING btree (modified_at);
-
-
---
--- Name: index_links_on_modified_at_uuid; Type: INDEX; Schema: public; Owner: -
---
-
-CREATE INDEX index_links_on_modified_at_uuid ON public.links USING btree (modified_at DESC, uuid);
+CREATE INDEX index_links_on_modified_at_and_uuid ON public.links USING btree (modified_at, uuid);
--
--
--- Name: index_logs_on_created_at; Type: INDEX; Schema: public; Owner: -
+-- Name: index_logs_on_created_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_logs_on_created_at ON public.logs USING btree (created_at);
+CREATE INDEX index_logs_on_created_at_and_uuid ON public.logs USING btree (created_at, uuid);
--
--
--- Name: index_logs_on_modified_at; Type: INDEX; Schema: public; Owner: -
+-- Name: index_logs_on_modified_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_logs_on_modified_at ON public.logs USING btree (modified_at);
-
-
---
--- Name: index_logs_on_modified_at_uuid; Type: INDEX; Schema: public; Owner: -
---
-
-CREATE INDEX index_logs_on_modified_at_uuid ON public.logs USING btree (modified_at DESC, uuid);
+CREATE INDEX index_logs_on_modified_at_and_uuid ON public.logs USING btree (modified_at, uuid);
--
--
--- Name: index_repositories_on_modified_at_uuid; Type: INDEX; Schema: public; Owner: -
+-- Name: index_repositories_on_created_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_repositories_on_modified_at_uuid ON public.repositories USING btree (modified_at DESC, uuid);
+CREATE INDEX index_repositories_on_created_at_and_uuid ON public.repositories USING btree (created_at, uuid);
+
+
+--
+-- Name: index_repositories_on_modified_at_and_uuid; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_repositories_on_modified_at_and_uuid ON public.repositories USING btree (modified_at, uuid);
--
--
--- Name: index_users_on_created_at; Type: INDEX; Schema: public; Owner: -
+-- Name: index_users_on_created_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_users_on_created_at ON public.users USING btree (created_at);
+CREATE INDEX index_users_on_created_at_and_uuid ON public.users USING btree (created_at, uuid);
--
--
--- Name: index_users_on_modified_at; Type: INDEX; Schema: public; Owner: -
---
-
-CREATE INDEX index_users_on_modified_at ON public.users USING btree (modified_at);
-
-
---
--- Name: index_users_on_modified_at_uuid; Type: INDEX; Schema: public; Owner: -
+-- Name: index_users_on_modified_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_users_on_modified_at_uuid ON public.users USING btree (modified_at DESC, uuid);
+CREATE INDEX index_users_on_modified_at_and_uuid ON public.users USING btree (modified_at, uuid);
--
CREATE UNIQUE INDEX index_users_on_uuid ON public.users USING btree (uuid);
+--
+-- Name: index_virtual_machines_on_created_at_and_uuid; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_virtual_machines_on_created_at_and_uuid ON public.virtual_machines USING btree (created_at, uuid);
+
+
--
-- Name: index_virtual_machines_on_hostname; Type: INDEX; Schema: public; Owner: -
--
--
--- Name: index_virtual_machines_on_modified_at_uuid; Type: INDEX; Schema: public; Owner: -
+-- Name: index_virtual_machines_on_modified_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_virtual_machines_on_modified_at_uuid ON public.virtual_machines USING btree (modified_at DESC, uuid);
+CREATE INDEX index_virtual_machines_on_modified_at_and_uuid ON public.virtual_machines USING btree (modified_at, uuid);
--
--
--- Name: index_workflows_on_modified_at_uuid; Type: INDEX; Schema: public; Owner: -
+-- Name: index_workflows_on_created_at_and_uuid; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_workflows_on_created_at_and_uuid ON public.workflows USING btree (created_at, uuid);
+
+
+--
+-- Name: index_workflows_on_modified_at_and_uuid; Type: INDEX; Schema: public; Owner: -
--
-CREATE INDEX index_workflows_on_modified_at_uuid ON public.workflows USING btree (modified_at DESC, uuid);
+CREATE INDEX index_workflows_on_modified_at_and_uuid ON public.workflows USING btree (modified_at, uuid);
--
('20211027154300'),
('20220224203102'),
('20220301155729'),
-('20220303204419');
+('20220303204419'),
+('20220401153101');
cancelled_at: ~
cancelled_by_user_uuid: ~
cancelled_by_client_uuid: ~
- created_at: <%= 3.minute.ago.to_s(:db) %>
- started_at: <%= 3.minute.ago.to_s(:db) %>
+ created_at: <%= 2.7.minute.ago.to_s(:db) %>
+ started_at: <%= 2.7.minute.ago.to_s(:db) %>
finished_at: ~
script: hash
repository: active/foo
state: Ready
uuid: zzzzz-d1hrv-1yfj6xkidf2muk3
owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
- created_at: <%= 3.1.minute.ago.to_s(:db) %>
+ created_at: <%= 2.9.minute.ago.to_s(:db) %>
components:
foo:
script: foo
# Helps test that clients cope with funny-shaped components.
# For an example, see #3321.
uuid: zzzzz-d1hrv-1yfj61234abcdk4
- created_at: <%= 2.minute.ago.to_s(:db) %>
+ created_at: <%= 4.minute.ago.to_s(:db) %>
owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
modified_by_client_uuid: zzzzz-ozdt8-brczlopd8u8d0jr
modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz
state: Ready
uuid: zzzzz-d1hrv-1yfj61234abcdk3
owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
- created_at: <%= 3.1.minute.ago.to_s(:db) %>
+ created_at: <%= 3.2.minute.ago.to_s(:db) %>
components:
part-one:
script_parameters:
uuid: zzzzz-d1hrv-1yfj6dcba4321k3
pipeline_template_uuid: zzzzz-p5p6p-aox0k0ofxrystgw
owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
- created_at: <%= 3.1.minute.ago.to_s(:db) %>
+ created_at: <%= 3.3.minute.ago.to_s(:db) %>
components:
part-one:
script_parameters:
uuid: zzzzz-d1hrv-ri9dvgkgqs9y09j
owner_uuid: zzzzz-tpzed-0fusedrivertest
pipeline_template_uuid: zzzzz-p5p6p-vq4wuvy84xvaq2r
- created_at: 2014-09-15 12:00:00
+ created_at: 2014-09-16 12:00:00
name: "pipeline instance owned by FUSE"
components:
foo:
uuid: zzzzz-d1hrv-scarxiyajtshq3l
owner_uuid: zzzzz-j7d0g-0000ownedbyfuse
pipeline_template_uuid: zzzzz-p5p6p-vq4wuvy84xvaq2r
- created_at: 2014-09-15 12:00:00
+ created_at: 2014-09-17 12:00:00
name: "pipeline instance in FUSE project"
components:
foo:
state: Complete
uuid: zzzzz-d1hrv-ju5ghi0i9z2kqc6
owner_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
- created_at: 2014-09-15 12:00:00
+ created_at: 2014-09-18 12:00:00
components:
foo:
script: foo
state: Complete
uuid: zzzzz-d1hrv-lihrbd0i9z2kqc6
owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
- created_at: 2014-09-15 12:00:00
+ created_at: 2014-09-19 12:00:00
components:
foo:
script: foo
name: Pipeline in public project with other objects elsewhere
pipeline_template_uuid: zzzzz-p5p6p-aox0k0ofxrystgw
state: Complete
- created_at: 2014-09-15 12:00:00
+ created_at: 2014-09-20 12:00:00
components:
foo:
script: foo
name: Pipeline in New state in publicly accessible project
pipeline_template_uuid: zzzzz-p5p6p-tmpltpublicproj
state: New
- created_at: 2014-09-15 12:00:00
+ created_at: 2014-09-21 12:00:00
components:
foo:
script: foo
name: Pipeline in New state in public project with objects elsewhere
pipeline_template_uuid: zzzzz-p5p6p-aox0k0ofxrystgw
state: New
- created_at: 2014-09-15 12:00:00
+ created_at: 2014-09-22 12:00:00
components:
foo:
script: foo
name: Pipeline in public project in New state with file type data class with objects elsewhere
pipeline_template_uuid: zzzzz-p5p6p-aox0k0ofxrystgw
state: New
- created_at: 2014-09-15 12:00:00
+ created_at: 2014-09-23 12:00:00
components:
foo:
script: foo
name: running_with_job
uuid: zzzzz-d1hrv-runningpipeline
owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
- created_at: <%= 3.1.minute.ago.to_s(:db) %>
- started_at: <%= 3.1.minute.ago.to_s(:db) %>
+ created_at: <%= 2.8.minute.ago.to_s(:db) %>
+ started_at: <%= 2.8.minute.ago.to_s(:db) %>
state: RunningOnServer
components:
foo:
uuid: zzzzz-d1hrv-twodonepipeline
owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
state: Complete
- created_at: <%= 3.minute.ago.to_s(:db) %>
+ created_at: <%= 2.5.minute.ago.to_s(:db) %>
started_at: <%= 2.minute.ago.to_s(:db) %>
finished_at: <%= 1.minute.ago.to_s(:db) %>
components:
controller: 'logs',
}
assert_response :success
- assert_equal('logs.event_type asc, logs.modified_at desc, logs.uuid',
+ assert_equal('logs.event_type asc, logs.modified_at desc, logs.uuid desc',
assigns(:objects).order_values.join(', '))
end
controller: 'logs',
}
assert_response :success
- assert_equal('logs.modified_at asc, logs.uuid',
+ assert_equal('logs.modified_at asc, logs.uuid desc',
assigns(:objects).order_values.join(', '))
end
controller: 'logs',
}
assert_response :success
- assert_equal('logs.modified_at asc, logs.event_type desc, logs.uuid',
+ assert_equal('logs.modified_at asc, logs.event_type desc, logs.uuid desc',
assigns(:objects).order_values.join(', '))
end
headers: auth(:admin)
assert_response :success
uuids = json_response['items'].collect { |i| i['uuid'] }
- assert_equal uuids, uuids.sort
+ assert_equal uuids, uuids.sort.reverse
end
def assert_link_classes_ascend(current_class, prev_class)