facebook / taste-tester

Software to manage a chef-zero instance and use it to test changes on production servers.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

state file gets confused

jaymzh opened this issue · comments

the state file gets the same key in it twice, sometimes;

{"port":5257,"ssl":false,"ssh":true,"logging":true,"ref":null,"ref":"426f245cc96aeaf4ef2aa8c274294db91566417c"}

Note 'ref' is there twice... in my case I think this is one of 2 problems causing tt to restart itself all the time.

Now I re-read the code in #116 , I am perplexed. I am not confident that it actually does anything. outside of a minor formatting change, this is assigning state = Mash.new only to be usually overwritten with a hash from JSON.parse.

The mash structure is cool, but I am not sure it helps here. I think a simpler solution to this problem is to flatten the key .to_s in the write method. How does that sound?

no, you silly man. That's what mashes do... they treat string and symbol keys as the same thing. That's literally the point of them, so it always collapses them and we only ever write out strings, and always ever read in strings without constantly converting manually.

I get that mashes handle keys differently. That's not the issue. The issue I'm raising is that simply assigning a new mash to state before trying to read a json file / falling back on an empty hash. The resulting data type of state is always a hash not a mash as you intended. Example:

$ irb
irb(main):001:0> require 'chef/mash'
=> true
irb(main):002:0> require 'json'
=> true
irb(main):003:0> m = Mash.new
=> {}
irb(main):004:0> m.class
=> Mash
irb(main):005:0> m = JSON.parse(File.read('/data/users/malmond/.taste-tester/prod/ref.json'))
=> {"port"=>5093, "ssl"=>true, "ssh"=>false, "logging"=>true, "bundle"=>"compatible", "ref"=>"bb50da1a52bd0e9e1ab1d03649ed27690445e092", "last_upload_time"=>"2019-08-15 10:24:41"}
irb(main):006:0> m.class
=> Hash
irb(main):007:0> m = {}
=> {}
irb(main):008:0> m.class
=> Hash

I don't think the code in #116 actually does anything. If anything, the solution here is even simpler:

     def write(key, val)
-      merge({ key => val })
+      merge({ key.to_s => val })
     end

This flattens all keys to strings, all the time.

Of course it's JSON... but it collapses all the keys. Every time. That's the point of mashes. What's written out is JSON, it's read into a mash, the keys merge properly. You CANNOT find a case with the current code where you'll get a :state and a "state" key, I'll bet money on it.

irb(main):002:0> m = Mash.new
=> {}
irb(main):003:0> m[:stuff] = 'bar'
=> "bar"
irb(main):004:0> m.merge!(JSON.parse('{"stuff": "wonk"}'))
=> {"stuff"=>"wonk"}

This is the whole reason that Chef wrote Mash. Let Mash do that for you so you're not doing it yourself.