【问题标题】:Complex validation in Jsonnet libraryJsonnet 库中的复杂验证
【发布时间】:2020-04-10 10:51:54
【问题描述】:

我要做的是创建一个 libsonnet 库,对输入进行一些复杂的验证,但我不确定如何在 libsonnet 文件中实现此功能,而不会返回 null

我正在尝试使用 Jsonnet 为 Hosted Graphite's Alerts API 生成 API 调用。想法是我们可以将所有警报存储在版本控制中,并在 CI / CD 管道中更新它们。我想防止错误,所以我根据上面的 API 规范定义了一些复杂的验证。我将以下内容另存为alerts.libsonnet

local alert_criteria_types = [
  'above',
  'below',
  'missing',
  'outside_bounds',
];

local notification_types_strings = [
  'state_change',
];

local notification_types_arrays = [
  'every',
  'state_change',
];

local on_query_failure_types = [
  'ignore',
  'notify',
  null,
];

{
  local HostedGraphiteAlerts = self,

  new(
    name,
    metric,
    alert_criteria_type,
    additional_alert_criteria={},
    additional_criteria={},
    expression='a',
    scheduled_mutes=[],
    notification_channels=['Email me'],
    notification_type=null,
    info=null,
    on_query_failure='notify',
  )::

    // Simple checks
    assert std.member(alert_criteria_types, alert_criteria_type) : "Input 'alert_criteria_type' is not one of the types: %s." % std.join(', ', alert_criteria_types);
    assert std.member(on_query_failure_types, on_query_failure) : "Input 'on_query_failure_type' is not one of the types: %s." % std.join(', ', on_query_failure_types);

    // Advanced checks
    if notification_type != null && std.isString(notification_type) then
        assert std.member(notification_types_strings, notification_type) : "Input 'notification_type' is not one of the types: %s." % std.join(', ', notification_types_strings);

    if notification_type != null && std.isArray(notification_type) then
      assert std.member(notification_types_arrays, notification_type[0]) : "Input 'notification_type' is not one of the types: %s." % std.join(', ', notification_types_arrays);

    if notification_type != null && std.isArray(notification_type) then
        assert std.member(notification_types_arrays, notification_type[0]) : "Input 'notification_type' is not one of the types: %s." % std.join(', ', notification_types_arrays);

    if notification_type != null && std.isArray(notification_type) && notification_type[0] == 'every' then
        assert notification_type[1] != null : "Input 'notification_type' cannot have an empty entry for 'time_in_minutes' for notification type 'every'.";

    if notification_type != null && std.isArray(notification_type) && notification_type[0] == 'every' then
        assert std.isNumber(notification_type[1]) : "Input 'notification_type' must have a JSON 'number' type for notification type 'every'.";

    // Main
    {
      name: name,
      metric: metric,
      alert_criteria: {
        type: alert_criteria_type,
      } + additional_alert_criteria,
      additional_criteria: additional_criteria,
      expression: expression,
      scheduled_mutes: scheduled_mutes,
      notification_channels: notification_channels,
      notification_type: notification_type,
      info: info,
      on_query_failure: on_query_failure,
    },
}

这通过了基本的jsonnetfmt 检查,但问题是当我像这样在alerts.jsonnet 文件中使用它时:

local alerts = (import 'hosted_graphite.libsonnet').alerts;

alerts.new(
  name='something',
  metric='some.graphite.metric',
  alert_criteria_type='below',
)

这只是返回null

$ jsonnet hosted_graphite/alerts.jsonnet
null

我知道这是因为它采用了第一个 assert 语句的值。但是这还能怎么做呢?

谢谢!

【问题讨论】:

    标签: jsonnet


    【解决方案1】:

    注意jsonnet 不是命令式语言,不要期望那些if 行会像脚本的一部分一样被评估。 将断言视为“虚拟”/不可见字段,必须始终评估为 true

    下面实现了(我认为)你所追求的:

    hosted_graphite.libsonnet

    local alert_criteria_types = [
      'above',
      'below',
      'missing',
      'outside_bounds',
    ];
    
    local notification_types_strings = [
      'state_change',
    ];
    
    local notification_types_arrays = [
      'every',
      'state_change',
    ];
    
    local on_query_failure_types = [
      'ignore',
      'notify',
      null,
    ];
    
    {
      local HostedGraphiteAlerts = self,
    
      new(
        name,
        metric,
        alert_criteria_type,
        additional_alert_criteria={},
        additional_criteria={},
        expression='a',
        scheduled_mutes=[],
        notification_channels=['Email me'],
        notification_type=null,
        info=null,
        on_query_failure='notify',
      )::
    
        // Main
        {
          name: name,
          metric: metric,
          alert_criteria: {
            type: alert_criteria_type,
          } + additional_alert_criteria,
          additional_criteria: additional_criteria,
          expression: expression,
          scheduled_mutes: scheduled_mutes,
          notification_channels: notification_channels,
          notification_type: notification_type,
          info: info,
          on_query_failure: on_query_failure,
    
          // Simple checks
          assert std.member(alert_criteria_types, self.alert_criteria.type) : (
            "Input 'alert_criteria_type' is not one of the types: %s." % std.join(', ', alert_criteria_types)
          ),
          assert std.member(on_query_failure_types, self.on_query_failure) : (
            "Input 'on_query_failure_type' is not one of the types: %s." % std.join(', ', on_query_failure_types)
          ),
    
          // Advanced checks:
          // - 1st line is a conditional that must be false ('A||B' construct) to get 2nd line evaluated
          // - 2nd line is the "final" type/value check, must be true
          assert (self.notification_type == null || !std.isString(self.notification_type) ||
                  std.member(notification_types_strings, self.notification_type)) : (
            "Input 'notification_type' string is not one of the types: %s." % std.join(', ', notification_types_strings)
          ),
          assert (self.notification_type == null || !std.isArray(self.notification_type) ||
                  std.member(notification_types_arrays, self.notification_type[0])) : (
            "Input 'notification_type' array is not one of the types: %s." % std.join(', ', notification_types_arrays)
          ),
          assert (self.notification_type == null || !std.isArray(self.notification_type) ||
                  self.notification_type != ['every', null]) : (
            "Input 'notification_type' cannot have an empty entry for 'time_in_minutes' for notification type 'every'."
          ),
          assert (self.notification_type == null || !std.isArray(self.notification_type) ||
                  [self.notification_type[0], std.isNumber(self.notification_type[1])] == ['every', true]) : (
            "Input 'notification_type' must have a JSON 'number' type for notification type 'every'."
          ),
        },
    }
    

    alerts.jsonnet

    local alerts = (import 'hosted_graphite.libsonnet');
    
    {
      a0: alerts.new(
        name='something',
        metric='some.graphite.metric',
        alert_criteria_type='below',
      ),
      a1: alerts.new(
        name='something',
        metric='some.graphite.metric',
        alert_criteria_type='below',
        notification_type='state_change',
      ),
      a2: alerts.new(
        name='something',
        metric='some.graphite.metric',
        alert_criteria_type='below',
        notification_type=['every', 10],
      ),
    }
    

    请注意,我使用的是 self.<field> 而不是函数参数,这是一个很好的模式,可以在评估断言的同时允许派生/覆盖。

    顺便说一句,我还建议查看 https://cuelang.org/,它与 jsonnet 在同一领域发挥作用类型检查是语言不可或缺的一部分。

    【讨论】:

    • 显然使用jsonnet alerts.jsonnet 来获取清单输出
    • 非常感谢@jjo!很好的解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-28
    • 2014-08-18
    • 1970-01-01
    • 2013-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多