Skip to content

Commit

Permalink
Add state provider status GraphQL API (#2199)
Browse files Browse the repository at this point in the history
  • Loading branch information
yngvar-antonsson authored Mar 14, 2024
1 parent 7e18073 commit 2df7034
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 7 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Added

- Twophase commit timeouts now can be set with env.

- New GraphQL API ``failover_state_provider_status`` to ping state provider connection.

-------------------------------------------------------------------------------
[2.9.0] - 2024-03-06
-------------------------------------------------------------------------------
Expand Down
48 changes: 48 additions & 0 deletions cartridge/lua-api/failover.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
local checks = require('checks')
local errors = require('errors')
local fun = require('fun')
local net_box = require('net.box')
local httpc = require('http.client')
local digest = require('digest')

local twophase = require('cartridge.twophase')
local topology = require('cartridge.topology')
Expand Down Expand Up @@ -351,12 +354,57 @@ local function resume()
return true
end

local --[[const]] PING_TIMEOUT = 3 -- seconds
--- Gets status of the state provider if stateful failover is enabled.
--
-- @function get_state_provider_status
--
-- @treturn table {"state-provider-url": bool status}
-- or empty table if there are no state provider
local function get_state_provider_status()
local failover_params = topology.get_failover_params(
confapplier.get_readonly('topology')
)
if failover_params.mode == 'stateful' then
if failover_params.state_provider == 'etcd2' then
local result = {}
local etcd_params = failover_params.etcd2_params
local etcd_uris = etcd_params.endpoints
local http_auth
if failover_params.etcd2_params.username ~= '' then
local credentials = etcd_params.username .. ":" .. etcd_params.password
http_auth = "Basic " .. digest.base64_encode(credentials)
end
for _, uri in ipairs(etcd_uris) do
local resp = httpc.head(uri, {
timeout = PING_TIMEOUT,
headers = {
['Authorization'] = http_auth,
},
})
result[uri] = resp.headers ~= nil
end
return result
elseif failover_params.state_provider == 'tarantool' then
local state_provider_uri = failover_params.tarantool_params.uri
local conn = net_box.connect(state_provider_uri, {
user = 'client',
password = failover_params.tarantool_params.password,
})
return {[state_provider_uri] = conn:ping({timeout = PING_TIMEOUT})}
end
end

return {}
end

return {
get_params = get_params,
set_params = set_params,
promote = promote,
pause = pause,
resume = resume,
get_state_provider_status = get_state_provider_status,
get_failover_enabled = get_failover_enabled, -- deprecated
set_failover_enabled = set_failover_enabled, -- deprecated
}
33 changes: 33 additions & 0 deletions cartridge/webui/api-failover.lua
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,21 @@ local gql_type_userapi = gql_types.object({
}
})

local gql_type_state_provider_status = gql_types.object({
name = 'StateProviderStatus',
description = 'Failover state provider status',
fields = {
uri = {
kind = gql_types.string.nonNull,
description = 'State provider uri',
},
status = {
kind = gql_types.boolean.nonNull,
description = 'State provider status',
},
}
})

local function get_failover_params(_, _)
local failover_params = lua_api_failover.get_params()
local masked_pwd = '******'
Expand Down Expand Up @@ -116,6 +131,14 @@ local function promote(_, args)
return lua_api_failover.promote({[replicaset_uuid] = instance_uuid}, opts)
end

local function get_state_provider_status(_, _)
local result = {}
for uri, status in pairs(lua_api_failover.get_state_provider_status()) do
table.insert(result, {uri = uri, status = status})
end
return result
end

local function init(graphql)

graphql.add_callback({
Expand Down Expand Up @@ -150,6 +173,15 @@ local function init(graphql)
callback = module_name .. '.get_failover_params',
})

graphql.add_callback({
prefix = 'cluster',
name = 'failover_state_provider_status',
doc = 'Get state provider status.',
args = {},
kind = gql_types.list(gql_type_state_provider_status).nonNull,
callback = module_name .. '.get_state_provider_status',
})

graphql.add_mutation({
prefix = 'cluster',
name = 'failover_params',
Expand Down Expand Up @@ -209,6 +241,7 @@ return {
set_failover_enabled = set_failover_enabled,
get_failover_params = get_failover_params,
set_failover_params = set_failover_params,
get_state_provider_status = get_state_provider_status,
promote = promote,
pause = pause,
resume = resume,
Expand Down
26 changes: 19 additions & 7 deletions doc/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ type Apicluster {
"""Show suggestions to resolve operation problems"""
suggestions: Suggestions

"""Get state provider status."""
failover_state_provider_status: [StateProviderStatus]!

"""List authorized users"""
users(
"""
Expand All @@ -41,24 +44,24 @@ type Apicluster {
username: String
): [User!]

"""Virtual buckets count in cluster"""
vshard_bucket_count: Int!
"""compression info about cluster"""
cluster_compression: ClusterCompressionInfo!

"""Get list of all registered roles and their dependencies."""
known_roles: [Role!]!

"""Virtual buckets count in cluster"""
vshard_bucket_count: Int!

"""Get automatic failover configuration."""
failover_params: FailoverAPI!

"""Get list of known vshard storage groups."""
vshard_known_groups: [String!]!
vshard_groups: [VshardGroup!]!

"""Validate config"""
validate_config(sections: [ConfigSectionInput]): ValidateConfigResult!

"""compression info about cluster"""
cluster_compression: ClusterCompressionInfo!
"""Get list of known vshard storage groups."""
vshard_known_groups: [String!]!

"""Get cluster config sections"""
config(sections: [String!]): [ConfigSection]!
Expand Down Expand Up @@ -1069,6 +1072,15 @@ type SpaceCompressionInfo {
space_name: String!
}

"""Failover state provider status"""
type StateProviderStatus {
"""State provider uri"""
uri: String!

"""State provider status"""
status: Boolean!
}

type Suggestions {
force_apply: [ForceApplySuggestion!]
restart_replication: [RestartReplicationSuggestion!]
Expand Down
39 changes: 39 additions & 0 deletions test/integration/failover_stateful_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -776,3 +776,42 @@ add('test_invalid_params', function(g)
return require('cartridge.confapplier').get_state()
end), 'RolesConfigured')
end)

local function get_status()
return S1:graphql({
query = [[{
cluster {
failover_state_provider_status {
uri
status
}
}
}]],
})
end

local test_cases = {
[g_stateboard] = "localhost:14401",
[g_etcd2] = "http://127.0.0.1:14001",
}
for group, uri in pairs(test_cases) do
group.test_get_state_provider_status = function(g)
helpers.retrying({}, function()
t.assert(g.client:get_session():get_coordinator())
end)

local resp = get_status()
t.assert_equals(resp['data']['cluster']['failover_state_provider_status'][1]['uri'], uri)
t.assert_equals(resp['data']['cluster']['failover_state_provider_status'][1]['status'], true)

-- stop state provider
g.state_provider:stop()

local resp = get_status()
t.assert_equals(resp['data']['cluster']['failover_state_provider_status'][1]['uri'], uri)
t.assert_equals(resp['data']['cluster']['failover_state_provider_status'][1]['status'], false)
g.state_provider:start()
end
end


0 comments on commit 2df7034

Please sign in to comment.