-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow custom insync? checking for resources #225
Comments
Not having a way of defining an Has any more thought been given to how else this might be implemented? A |
@seanmil With the restrictions specified in the description, I'm currently having no good idea on how to implement that (which usually means I'll have to compromise on the reqs eventually). Providing a list of specific usecases would definitely help either way (finding better ideas and putting pressure on the requirements). |
The current use case I have in mind is something like the This is not (as far as I can tell) possible to currently achieve, even via abuse of canonicalize, but would be simple to achieve if a It might be possible with a sophisticated enough ASN.1/LDAP style filter/query syntax, but I'm not sure that complexity of designing and building something like that outweighs having a Here is what I propose: add support for a This doesn't preclude adding a set of simple attribute comparisons later (e.g. min, max, subset, etc.) that might be useful for folks to use in the simple case, but allows all cases to be handled immediately. |
Alternatively, maybe name it something like |
Thinking about a resource-level |
Frédéric Lespez writes in trying to implement a upper boundary to a property. |
@seanmil , I've added another idea to the description: adding a bunch of comparator objects that get packed into the catalog like What do you think? |
Just my two cents: About parameters (attributes declared with this behavior):
So for now, this restricts the parameters to influence how the provider behaves only :
In the use cases we are discussing, the problem is how to extend the way we decide that the 'is' and 'should' states are the same. Currently, only equality of properties (ignoring parameters) is supported. We need a way:
Concerning the first alternative (comparison_operator key to the attribute definition) Concerning the third alternative (pass in a marker object similar to Deferred) In the other use case (inclusive vs. minimum for group state), how will we use it ? This way of doing things we also have an impact Puppet wide: we will be able to apply this kind of markers on any resource handled by Puppet, won't we ? Concerning the second alternative (Add a feature that allows the provider to have a compare(a,b) method) : Does it really violate one of the design principles of the Resource API, namely : no provider code being needed to run for operations not related to the actual management of the resource. I think it is related: The provider is in charge of reporting the real state of resources (through the get method). It does mean that the only provider is able to describe the real state of a resource. Doesn't it also mean that only the provider is able to tell if the 'is' and 'should' states are the same ? The other advantage is that it will extend parameter usage in a way that is coherent with how other Puppet Resources (like User) are used. So, for me, adding a method called equal?(context, is, should) returning true if the 'is' and 'should' states are the same and false otherwise, triggering a call of the set() method would be a welcome addition. Hope it helps. |
Adding a use case: I'm working on a provider that implements calls to an API. E.g.:
|
You can do this today by sorting the arrays from your API when |
Adding my use-case... I'm trying to write provider(s) for rundeck via its API. The type I'm currently working on is for managing SCM configs. rundeck_scm_config { 'SomeProject/export/git-export': # 3 project, integration, and 'type' namevars.
ensure => present,
enabled => true,
config = {
'somefieldicareabout' => 'value1',
'someotherconfig' => 'value2',
# The API supports many other fields, but I don't care about them, or are happy with defaults, so I don't want to specify them here.
},
} The allowed config is based on the version of rundeck, and its scm plugin versions etc. For that reason, the type has a hash What I'd like, is to be able to specify just some of the keys in |
custom_insync? for the Resource APIA strict property-by-property equivalence comparison is sometimes too strict; being able to write a custom
Requirements:
Design Considerations
Implementation
Minimum and Inclusive Arrays & HashesPlacing the logic in the custom comparison method instead of in the type definition could allow for resource to have a property be treated as either minimum or inclusive depending on the manifest definition. For example: # Resource Definitions in test.pp
resource_with_arrays { 'Minimum Array':
the_array => ['a', 'b'],
}
resource_with_arrays { 'Inclusive Array':
the_array => ['a', 'b'],
# An existing pattern in Puppet resources is to specify a force flag when you want
# to ensure that precise comparisons of arrays are used, instead of a minimal array
force => true,
} # Custom comparison in the provider
def insync?(context, name, attribute_name, is_hash, should_hash)
case attribute_name
when :the_array
if should_hash[:force]
context.notice("Checking an order independent array")
return is_hash[attribute_name].sort == should_hash[attribute_name].sort
else
context.notice("Checking a subset match array")
return (is_hash[attribute_name] & should_hash[attribute_name]).sort == should_hash[attribute_name].sort
end
# No further statements are necessary; if the custom insync? in the provider
# does not return true or false, the comparison for the attribute will default
# to the normal insync check in Puppet::Property
end
end This behavior could also be implemented to always be minimal or inclusive, depending on the provider, and could be used for hashes as well as arrays (though hash comparisons can be trickier if there's nested hashes). Minimum and Maximum Versions/NumbersPlacing the logic in the custom comparison method could allow for resource to properties for minimum/maximum/exact versions. For example: # Resource Definitions in test.pp
# If version is not specified, it defaults to ''
resource_with_version_properties { 'Exact Version':
version => '1.2.3',
}
resource_with_version_properties { 'Minimum Version':
minimum_version => '1.2.3',
}
resource_with_version_properties { 'Maximum Version':
maximum_version => '1.2.3',
}
resource_with_version_properties { 'Custom Version String':
maximum_version => '>= 1.2.3',
} # Custom comparison in the provider for multiple version properties
# For particularly complex insync checks, it can be useful to break
# the check into a separate method for testing and readability.
def version_insync?(context, is_hash, should_hash)
# When version is not specified, it is expected you're passing either/both
# the minimum & maximum version bounds; if neither is specified, then we
# don't care about version for this declaration and don't trigger a change.
if should_hash[:version] == ''
if should_hash[:minimum_version] || should_hash[:maximum_version]
context.notice('Checking a min/max version')
meets_minimum_expectation = Gem::Version.new(is_hash[:version]) >= Gem::Version.new(should_hash[:minimum_version]) unless should_hash[:minimum_version].nil?
meets_maximum_expectation = Gem::Version.new(is_hash[:version]) <= Gem::Version.new(should_hash[:maximum_version]) unless should_hash[:maximum_version].nil?
if should_hash[:minimum_version] && should_hash[:maximum_version]
return meets_minimum_expectation && meets_maximum_expectation
elsif should_hash[:minimum_version]
return meets_minimum_expectation
else
return meets_maximum_expectation
end
else
return true
end
# If a version is specified and matches a dependency string, use
# Gem::Dependency to determine if it's insync. Otherwise, default
# to normal insync comparison.
elsif !should_hash[:version].nil? && should_hash[:version].match?(%r{^\D\s+})
context.notice("Checking a custom version string")
return Gem::Dependency.new('', should_hash[:version]).match?('', is_hash[:version])
end
end
# Custom comparison in the provider called for each property
def insync?(context, name, attribute_name, is_hash, should_hash)
case attribute_name
when :version
return version_insync?(context, is_hash, should_hash)
end
end Case Insensitive String ComparisonsPlacing the logic in the custom comparison method could allow for resource to have a property for which does not care about the casing of a string. For example: # Resource Definition in test.pp
resource_with_strings { 'example':
case_sensitive_string => 'FooBar',
case_insensitive_string => 'foobar',
} # Custom comparison in the provider
def insync?(context, name, attribute_name, is_hash, should_hash)
case attribute_name
when :case_insensitive_string
return is_hash[attribute_name].downcase == should_hash[attribute_name].downcase
end DSC Specific Notes
SummaryThis implementation should be scoped to new/updated resources which specify a feature flag and implement a custom method in their provider - while not effecting current behavior or simple providers. Furthermore, it should enable as much complexity of comparison as a type requires without being overly opinionated or adding too much maintenance burden to the Resource API itself; only types which utilize the feature should have an increased maintenance cost (commensurate with the benefit of being able to accurately manage the resource). I do not have strong opinions on the name of the flag or method, I used Are there other thoughts or feedback on this? Implementing a solution is becoming a priority for the DSC work as numerous critical resources cannot be effectively managed with a strict property value comparison check. That being said, whatever is implemented here must work for more generalized use cases as mentioned up-thread and in the requirements. |
Folks watching the thread: I went back with some input from @DavidS and updated the comment to be a bit more clear, separate out requirements, considerations, and implementation more usefully, and provide some examples for how I'm doing some investigation for the next couple days and will update that comment again a few times and drop a new comment downthread when it's "finalized." |
\o It's been a while, but for those of you still watching the thread, we have a functional draft PR up which implements custom insync for Resource API types that specify the If you have any thoughts or feedback, I'd love to hear it, but the way things worked out, from the perspective of a developer using the Resource API to define a resource that needs custom insync you would need to:
Our hope is that by requiring minimal extra handling in the provider's |
(GH-225) Add support for custom insync
@michaeltlombardi Thanks for your PR. I am currently testing the new I found a little bug: |
Use Case
For some use-cases - like specifying upper/lower bounds on values or set memberships ("for instance, I have a use-case where I want things to be treated as in-sync as long as the array specified in Puppet is a equal to or a subset of the array in the response from the API") - the Resource API type schema is not expressive enough. This leads to nasty workarounds, like munging up the desired values in canonicalize.
The current restriction is so that anyone dealing with raw resource data doesn't need to run the provider code to understand whether something is insync or not.
Describe the Solution You Would Like
???
Describe Alternatives You've Considered
comparison_operator
key (with values likeeq
,min
,max
) to the attribute definition.See ASN.1/LDAP schemas for an example of data-defined comparisons.
feature
that allows the provider to have acompare(a,b)
method.This would violate one of the design principles of the Resource API, namely no provider code being needed to run for operations not related to the actual management of the resource.
Deferred
that encapsulates the comparison:age => LessThan($upper_limit)
that is sent to the agent and evaluated there.
Additional Context
Originally filed as https://tickets.puppetlabs.com/browse/MODULES-9574.
The text was updated successfully, but these errors were encountered: