cloud66-oss / copper

A configuration file validator for Kubernetes.
https://help.cloud66.com/copper/
Apache License 2.0
274 stars 16 forks source link

problem reading annotations, e.g. can't modify frozen String (FrozenError) #13

Closed carltonmason closed 5 years ago

carltonmason commented 5 years ago

Hello Again,

I am trying to validate that a simple Ingress .yaml contains a certain ingress class in its annotations yet, I get:

/usr/local/lib/ruby/gems/2.5.0/gems/jsonpath-0.9.8/lib/jsonpath.rb:39:in `initialize': can't modify frozen String (FrozenError)

I think it has something to do with the . and / in my annotation values.

Here is my input yaml:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: eurekap
spec:
  backend:
    serviceName: public-gdpr-data-deletion
    servicePort: 443

Here is my Copper rule file:

rule IngressAnnotation ensure {
    fetch("$.metadata.annotations.kubernetes.io/ingress.class").first == "eurekap"
}

And when I run Copper, I get the following error:

Carltons-MacBook-Pro:rendered-armada.d ckmason$ copper check  --rules ingress.cop --file ingress-good.yaml
Validating part 0
Traceback (most recent call last):
    23: from /usr/local/lib/ruby/gems/2.5.0/bin/copper:23:in `<main>'
    22: from /usr/local/lib/ruby/gems/2.5.0/bin/copper:23:in `load'
    21: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/bin/copper:7:in `<top (required)>'
    20: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/bin/copper:121:in `<module:Copper>'
    19: from /usr/local/lib/ruby/gems/2.5.0/gems/thor-0.20.3/lib/thor/base.rb:466:in `start'
    18: from /usr/local/lib/ruby/gems/2.5.0/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
    17: from /usr/local/lib/ruby/gems/2.5.0/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
    16: from /usr/local/lib/ruby/gems/2.5.0/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
    15: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/bin/copper:73:in `check'
    14: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/bin/copper:73:in `each_with_index'
    13: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/bin/copper:73:in `each'
    12: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/bin/copper:76:in `block in check'
    11: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/bin/copper:85:in `validate'
    10: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/lib/copper/copper.rb:12:in `execute'
     9: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/lib/copper/root.rb:5:in `value'
     8: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/lib/copper/root.rb:5:in `each'
     7: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/lib/copper/root.rb:6:in `block in value'
     6: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/lib/copper/rule_definition.rb:11:in `value'
     5: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/lib/copper/logic.rb:6:in `value'
     4: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/lib/copper/comparison.rb:5:in `value'
     3: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/lib/copper/expression.rb:5:in `value'
     2: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/lib/copper/functions/fetch.rb:14:in `value'
     1: from /usr/local/lib/ruby/gems/2.5.0/gems/c66-copper-0.0.7/lib/copper/functions/fetch.rb:14:in `new'
/usr/local/lib/ruby/gems/2.5.0/gems/jsonpath-0.9.8/lib/jsonpath.rb:39:in `initialize': can't modify frozen String (FrozenError)

If I modify both my .yaml and the rule to get rid of the slashes and dots in the annotation element, it works. I can't use that as a solution though. So, its got something to do with the format of my annotation and not sure how to get it to read them properly.

For example, if I modify my .yaml and rule to change my annotation from kubernetes.io/ingress.class to ingress-class it works.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    ingress-class: eurekap
spec:
  backend:
    serviceName: public-gdpr-data-deletion
    servicePort: 443

The Copper rule to be:

rule IngressAnnotation ensure {
    fetch("$.metadata.annotations.ingress-class").first == "eurekap"
}

Then it works:

Carltons-MacBook-Pro:rendered-armada.d ckmason$ copper check  --rules ingress.cop --file ingress-good.yaml
Validating part 0
    IngressAnnotation - PASS

Thanks for your help.

docwhat commented 5 years ago

I wonder if .class is the cause of the problem. It's a reserved word in Ruby and a lot of other languages.

What happens if you use the bracket-style for the child node? i.e., "$.metadata.annotations.ingress['class']"

carltonmason commented 5 years ago

Thanks @docwhat, I tried that and it is better as I no longer get the "FrozenError" but the rule assertion fails. Getting closer...

My yaml input:

rule IngressAnnotation ensure {
    // fetch("$.metadata.annotations.kubernetes.io/ingress.class").first == "eurekap" // Doesn't work, error: can't modify frozen String (FrozenError)
    fetch("$.metadata.annotations.ingress['class']").first == "eurekap"  // The assertion fails "IngressAnnotation - FAIL" better than the frozen string error...

}

My rule file:

rule IngressAnnotation ensure {
    // fetch("$.metadata.annotations.kubernetes.io/ingress.class").first == "eurekap" // Doesn't work, error: can't modify frozen String (FrozenError)
    fetch("$.metadata.annotations.ingress['class']").first == "eurekap"  // The assertion fails "IngressAnnotation - FAIL" better than the frozen string error...

}

And the result of running both in Copper:

 copper check  --rules ingress.cop --file ingress-good.yaml
Validating part 0
    IngressAnnotation - FAIL
khash commented 5 years ago

Copper uses JSONPath format in which . has a special meaning. You can use [ to refernece attributes better. For more details, please see https://goessner.net/articles/JsonPath/

docwhat commented 5 years ago

D'oh! I missed that ingress.class was a single path component.

@carltonmason

Given:

metadata:
  annotations:
    kubernetes.io/ingress.class: eurekap

You should use:

"$.metadata.annotations['kubernetes.io/ingress.class']"
carltonmason commented 5 years ago

@docwhat that worked! Thanks.