Skip to content

Commit 5467dfa

Browse files
authored
Merge pull request #5691 from victormlg/CFE-3641-is_type
Added is_type policy function
2 parents f2982bb + 7e0affc commit 5467dfa

File tree

3 files changed

+221
-26
lines changed

3 files changed

+221
-26
lines changed

examples/is_type.cf

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#+begin_src cfengine3
2+
body common control
3+
{
4+
bundlesequence => { "example" };
5+
}
6+
7+
bundle agent example
8+
{
9+
vars:
10+
"foo"
11+
data => '{ "bar": true }';
12+
"a" string => "hello";
13+
classes:
14+
"isdata"
15+
expression => is_type("foo", "data");
16+
"isdata_boolean"
17+
expression => is_type("foo[bar]", "data boolean");
18+
"isstring"
19+
expression => is_type("a", "string");
20+
"isint"
21+
expression => is_type("a", "int");
22+
23+
24+
reports:
25+
isdata::
26+
"'foo' is of type 'data'";
27+
isdata_boolean::
28+
"'foo[bar]' is of type 'data boolean'";
29+
isstring::
30+
"'a' is of type 'string'";
31+
isint::
32+
"'a' is of type 'int'";
33+
}
34+
35+
#+end_src
36+
#############################################################################
37+
#+begin_src example_output
38+
#@ ```
39+
#@ R: 'foo' is of type 'data'
40+
#@ R: 'foo[bar]' is of type 'data boolean'
41+
#@ R: 'a' is of type 'string'
42+
#@ ```
43+
#+end_src

libpromises/evalfunction.c

Lines changed: 89 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5276,38 +5276,21 @@ static FnCallResult FnCallFold(EvalContext *ctx,
52765276

52775277
/*********************************************************************/
52785278

5279-
static FnCallResult FnCallDatatype(EvalContext *ctx, ARG_UNUSED const Policy *policy, const FnCall *fp, const Rlist *finalargs)
5279+
static char *DataTypeStringFromVarName(EvalContext *ctx, const char *var_name, bool detail)
52805280
{
5281-
assert(fp != NULL);
5282-
assert(fp->name != NULL);
5281+
assert(var_name != NULL);
52835282

5284-
if (finalargs == NULL)
5285-
{
5286-
Log(LOG_LEVEL_ERR,
5287-
"Function %s requires variable identifier as first argument",
5288-
fp->name);
5289-
return FnFailure();
5290-
}
5291-
const char* const var_name = RlistScalarValue(finalargs);
5292-
5293-
VarRef* const var_ref = VarRefParse(var_name);
5283+
VarRef *const var_ref = VarRefParse(var_name);
52945284
DataType type;
52955285
const void *value = EvalContextVariableGet(ctx, var_ref, &type);
52965286
VarRefDestroy(var_ref);
52975287

5298-
/* detail argument defaults to false */
5299-
bool detail = false;
5300-
if (finalargs->next != NULL)
5301-
{
5302-
detail = BooleanFromString(RlistScalarValue(finalargs->next));
5303-
}
5304-
53055288
const char *const type_str =
53065289
(type == CF_DATA_TYPE_NONE) ? "none" : DataTypeToString(type);
53075290

53085291
if (!detail)
53095292
{
5310-
return FnReturn(type_str);
5293+
return SafeStringDuplicate(type_str);
53115294
}
53125295

53135296
if (type == CF_DATA_TYPE_CONTAINER)
@@ -5339,15 +5322,87 @@ static FnCallResult FnCallDatatype(EvalContext *ctx, ARG_UNUSED const Policy *po
53395322
subtype_str = "null";
53405323
break;
53415324
default:
5342-
Log(LOG_LEVEL_ERR,
5343-
"Function %s failed to get subtype of type data", fp->name);
5344-
return FnFailure();
5325+
return NULL;
53455326
}
53465327

5347-
return FnReturnF("%s %s", type_str, subtype_str);
5328+
return StringConcatenate(3, type_str, " ", subtype_str);
5329+
}
5330+
return StringConcatenate(2, "policy ", type_str);
5331+
}
5332+
5333+
static FnCallResult FnCallDatatype(EvalContext *ctx, ARG_UNUSED const Policy *policy, const FnCall *fp, const Rlist *finalargs)
5334+
{
5335+
assert(fp != NULL);
5336+
assert(fp->name != NULL);
5337+
5338+
if (finalargs == NULL)
5339+
{
5340+
Log(LOG_LEVEL_ERR,
5341+
"Function %s requires variable identifier as first argument",
5342+
fp->name);
5343+
return FnFailure();
5344+
}
5345+
const char *const var_name = RlistScalarValue(finalargs);
5346+
5347+
/* detail argument defaults to false */
5348+
bool detail = false;
5349+
if (finalargs->next != NULL)
5350+
{
5351+
detail = BooleanFromString(RlistScalarValue(finalargs->next));
5352+
}
5353+
char *const output_string = DataTypeStringFromVarName(ctx, var_name, detail);
5354+
5355+
if (output_string == NULL)
5356+
{
5357+
Log(LOG_LEVEL_ERR, "Function %s could not parse var type",
5358+
fp->name);
5359+
return FnFailure();
5360+
}
5361+
5362+
return FnReturnNoCopy(output_string);
5363+
}
5364+
5365+
/*********************************************************************/
5366+
5367+
static FnCallResult FnCallIsDatatype(EvalContext *ctx, ARG_UNUSED const Policy *policy, const FnCall *fp, const Rlist *finalargs)
5368+
{
5369+
assert(fp != NULL);
5370+
assert(fp->name != NULL);
5371+
5372+
// check args
5373+
const Rlist *const var_arg = finalargs;
5374+
if (var_arg == NULL)
5375+
{
5376+
Log(LOG_LEVEL_ERR, "Function %s requires a variable as first argument",
5377+
fp->name);
5378+
return FnFailure();
5379+
}
5380+
5381+
assert(finalargs != NULL); // assumes finalargs is already checked by var_arg
5382+
const Rlist *const type_arg = finalargs->next;
5383+
if (type_arg == NULL)
5384+
{
5385+
Log(LOG_LEVEL_ERR, "Function %s requires a type as second argument",
5386+
fp->name);
5387+
return FnFailure();
53485388
}
53495389

5350-
return FnReturnF("policy %s", type_str);
5390+
const char *const var_name = RlistScalarValue(var_arg);
5391+
const char *const type_name = RlistScalarValue(type_arg);
5392+
bool detail = StringContainsChar(type_name, ' ');
5393+
5394+
char *const type_string = DataTypeStringFromVarName(ctx, var_name, detail);
5395+
5396+
if (type_string == NULL)
5397+
{
5398+
Log(LOG_LEVEL_ERR, "Function %s could not determine type of the variable '%s'",
5399+
fp->name, var_name);
5400+
return FnFailure();
5401+
}
5402+
const bool matching = StringEqual(type_name, type_string);
5403+
free(type_string);
5404+
5405+
return FnReturnContext(matching);
53515406
}
53525407

53535408
/*********************************************************************/
@@ -10303,6 +10358,12 @@ static const FnCallArg DATATYPE_ARGS[] =
1030310358
{CF_BOOL, CF_DATA_TYPE_OPTION, "Enable detailed type decription"},
1030410359
{NULL, CF_DATA_TYPE_NONE, NULL}
1030510360
};
10361+
static const FnCallArg IS_DATATYPE_ARGS[] =
10362+
{
10363+
{CF_ANYSTRING, CF_DATA_TYPE_STRING, "Variable identifier"},
10364+
{CF_ANYSTRING, CF_DATA_TYPE_STRING, "Type"},
10365+
{NULL, CF_DATA_TYPE_NONE, NULL}
10366+
};
1030610367

1030710368

1030810369
/*********************************************************/
@@ -10715,6 +10776,8 @@ const FnCallType CF_FNCALL_TYPES[] =
1071510776
// Datatype functions
1071610777
FnCallTypeNew("type", CF_DATA_TYPE_STRING, DATATYPE_ARGS, &FnCallDatatype, "Get type description as string",
1071710778
FNCALL_OPTION_VARARG, FNCALL_CATEGORY_DATA, SYNTAX_STATUS_NORMAL),
10779+
FnCallTypeNew("is_type", CF_DATA_TYPE_STRING, IS_DATATYPE_ARGS, &FnCallIsDatatype, "Compare type of variable with type",
10780+
FNCALL_OPTION_VARARG, FNCALL_CATEGORY_DATA, SYNTAX_STATUS_NORMAL),
1071810781

1071910782
FnCallTypeNewNull()
1072010783
};
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
body common control
2+
{
3+
bundlesequence => { test, check };
4+
version => "1.0";
5+
}
6+
7+
bundle agent test
8+
{
9+
meta:
10+
"description" -> { "CFE-3641" }
11+
string => "Test policy function is_type";
12+
}
13+
14+
bundle agent check
15+
{
16+
vars:
17+
"mystring" string => "one";
18+
"myint" int => "1";
19+
"myreal" real => "0.2";
20+
"myslist" slist => { "one", "two", "three" };
21+
"myilist" ilist => { "1", "2", "3" };
22+
"myrlist" rlist => { "0.1", "0.2", "0.3" };
23+
"mydata" data => '{ "dstring": "two", "dint": 2, "darray": [1,2,3], "dreal": 0.3, "dbool": true, "dnull": null }';
24+
25+
classes:
26+
"isstring"
27+
expression => is_type("mystring", "string");
28+
"isint"
29+
expression => is_type("myint", "int");
30+
"isreal"
31+
expression => is_type("myreal", "real");
32+
"isslist"
33+
expression => is_type("myslist", "slist");
34+
"isilist"
35+
expression => is_type("myilist", "ilist");
36+
"isrlist"
37+
expression => is_type("myrlist", "rlist");
38+
"ispolicystring"
39+
expression => is_type("mystring", "policy string");
40+
"ispolicyint"
41+
expression => is_type("myint", "policy int");
42+
"ispolicyreal"
43+
expression => is_type("myreal", "policy real");
44+
"ispolicyslist"
45+
expression => is_type("myslist", "policy slist");
46+
"ispolicyilist"
47+
expression => is_type("myilist", "policy ilist");
48+
"ispolicyrlist"
49+
expression => is_type("myrlist", "policy rlist");
50+
"isdata"
51+
expression => is_type("mydata", "data");
52+
"isdataobject"
53+
expression => is_type("mydata", "data object");
54+
"isdatastring"
55+
expression => is_type("mydata[dstring]", "data string");
56+
"isdataint"
57+
expression => is_type("mydata[dint]", "data int");
58+
"isdataarray"
59+
expression => is_type("mydata[darray]", "data array");
60+
"isdatareal"
61+
expression => is_type("mydata[dreal]", "data real");
62+
"isdataboolean"
63+
expression => is_type("mydata[dbool]", "data boolean");
64+
"isdatanull"
65+
expression => is_type("mydata[dnull]", "data null");
66+
67+
"isnottype"
68+
expression => not(is_type("mystring", "int"));
69+
"containerisnottype"
70+
expression => not(is_type("mydata[darray]", "string"));
71+
"isnotcontainer"
72+
expression => not(is_type("myreal", "data array"));
73+
"containerisnotcontainer"
74+
expression => not(is_type("mydata[dbool]", "data int"));
75+
76+
77+
"ok"
78+
and => { "isstring", "isint", "isreal", "isslist", "isilist", "isrlist",
79+
"ispolicystring", "ispolicyint", "ispolicyreal", "ispolicyslist", "ispolicyilist",
80+
"isdata", "isdataobject", "isdatastring", "isdataint", "isdataarray",
81+
"isdatareal", "isdataboolean", "isdatanull", "isnottype", "containerisnottype",
82+
"isnotcontainer", "containerisnotcontainer" };
83+
84+
reports:
85+
ok::
86+
"$(this.promise_filename) Pass";
87+
!ok::
88+
"$(this.promise_filename) FAIL";
89+
}

0 commit comments

Comments
 (0)