Skip to content
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

Add representations of various system entities #6

Merged
merged 10 commits into from
Jul 2, 2013

Conversation

movitto
Copy link
Contributor

@movitto movitto commented Jun 24, 2013

No description provided.

@coveralls
Copy link

Coverage Status

Coverage remained the same when pulling 056bed5 on movitto:master into 00c2ad0 on ManageIQ:master.

@movitto
Copy link
Contributor Author

movitto commented Jun 25, 2013

Changes made / pushed

@coveralls
Copy link

Coverage Status

Coverage remained the same when pulling 895328b on movitto:master into 00c2ad0 on ManageIQ:master.

@coveralls
Copy link

Coverage Status

Coverage remained the same when pulling 895328b on movitto:master into 00c2ad0 on ManageIQ:master.

FileUtils.mkdir @mountpoint unless File.directory? @mountpoint

run "/usr/bin/mount #{self.path} #{@mountpoint}"
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not clear what the difference is between the path parameter and the path method. Should they be renamed for clarity?

@jrafanie
Copy link
Member

@movitto The travis build failed, not sure why.

@brandondunne @Fryguy @chessbyte
Is it too early to separate out the Redhat specific binary locations?

Does it make sense to extract the paths to various binaries into another location, such as a module constants like
LinuxAdmin::BinaryLocations::Redhat::SERVICE? For example, there are many instances of /usr/bin/systemctl, /usr/sbin/service, mount, etc., some of these may or may not exist or reside elsewhere for other distributions. I can envision having a common API on top that normalizes the commands/locations.

Maybe it makes sense to do this after we have substantial use of more than 3 binaries and have a better sense of how to separate this.

@Fryguy
Copy link
Member

Fryguy commented Jun 25, 2013

@movitto I have some style comments, and I'll put those inline, but I had a few general questions and comments first...

  • Needs specs around all new methods.
  • It seems the specs that you do have actually shell out. The tests should not shell out at all. There are ways to stub the run method with expected inputs to verify that it will shell out, and to mock the results. You can see the existing specs for examples.
  • You've added new classes, whereas the existing things are modules. I actually prefer the class approach and @brandondunne and I have talked about converting the existing ones to classes. However, I'm not sure about mixing and matching right now. It might be preferable to convert the existing ones to classes first. I'll leave that one up to @brandondunne.
  • When using the run command, it's preferable to pass command line parameters as params instead of using string interpolation. String interpolation leads to potential command-line-injection attacks. Example of params usage
  • I don't think we need the copyright notices on each individual file. Having the LICENSE file at the root includes all of that information. In addition, the comment describing the file at the top of the file is redundant, so I don't think it's needed.

Although I have a lot of comments, I just wanted to say I think this is great work, and is a great start to adding a lot of functionality to LinuxAdmin!


def self.local
(Dir.glob('/dev/hd[a-z]') +
Dir.glob('/dev/sd[a-z]')).collect { |d|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@movitto style: multiline blocks should use do/end. @Fryguy is planning on forking one of the ruby style guides and working on what our group's style should be. We should always prefer readability over the style guide though, so keep in mind it's fine (or even great) to have an opinion that readability dictates the code should look different than the style guide.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be a single Dir.glob call with Dir.glob('/dev/[hs]d[a-z]')

@Fryguy
Copy link
Member

Fryguy commented Jun 25, 2013

@movitto Oh, one more thing is that I noticed most of the commands prefix with /usr/bin. Is there a reason for this? In the other modules we just left the command bare, especially since we couldn't be sure where the command may be installed.

@mountpoint = path
@mountpoint =
"/mnt/#{disk.path.split(File::SEPARATOR).last}#{id}" if path.nil?
FileUtils.mkdir @mountpoint unless File.directory? @mountpoint
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap the arguments in parens () especially when you have an "unless" condition in the middle.

@jrafanie
Copy link
Member

@movitto Looks like a great start. Other than our style nitpicks, the only major things are the shelling out in the specs and that the specs appear much smaller than the code, so I'm not sure we have all the code covered by specs.

@Fryguy
Copy link
Member

Fryguy commented Jun 25, 2013

@movitto I don't understand the purpose of the .local methods. The purpose of LinuxAdmin is for interacting with a local system, so they seem kind of unnecessary. I would expect most, if not all, of that logic could go into the .initialize methods directly or delay-called in a method after initialization.

@movitto
Copy link
Contributor Author

movitto commented Jun 25, 2013

Updated:

  • incorporated all style/maintainability feedback
  • changed all invocations of 'run' to the proper usage
  • more specs coming soon
  • mocking / stubbing all command line output would be somewhat tedious / error prone as ultimately this could take care of alot of system-administration tasks. I agree not everything should be shelled out and some of it can be mock'd, but it isn't unheard of to run the test suite against the local environmnet (chroot'd if need be) and it's good to verify actual results. I can mock everything but it's going to add overhead to all of this.
  • I can look into moving the existing modules over to the OOD based approach going forward
  • In general RH encourages a copyright header on every source file (stuff like templates, etc usually are excluded). Every project I've worked on has followed this policy.
  • The reason for full paths: https://www.owasp.org/index.php/Command_Injection#Example_3 (s/APPHOME/PATH)
  • +1 to the idea of abstracting the paths using constants, I'll do that going forward (and edit the current ones as well)
  • I'm fine w/ doing whatever to check the exitstatus ; tbh checking if it == 0 is pretty universal / understood in any language
  • The 'local' methods make the whole system a bit more object-oriented. Coupling the constructors to the local system is very limiting, it does not allow us to use those same classes to represent anything else. For flexability purposes, having an external initializer gives us a little bit more leverage w/ what this all can be used for

Other thoughts?

@coveralls
Copy link

Coverage Status

Coverage remained the same when pulling bfb4852 on movitto:master into 00c2ad0 on ManageIQ:master.

@jrafanie
Copy link
Member

@movitto It's looking good.

A few thoughts on your comments.

mocking/stubbing output: I would say it would make sense to mock/stub shelling out whenever possible and if it becomes too brittle, unreadable, etc., we find another solution... such as more abstraction to hide the nastiness or just shell out. Let's let the code/test tell us we're doing it right/wrong.

If the full paths are encapsulated into modules as constants which you +1, it doesn't matter if they're fully qualified as we can change them in one place if FQ paths are not what we want later on.

I'm fine with dealing with shell commands exiting with 0 as a true value in ruby. My concern is if we check the rc for 0 all over the place, it might make sense to have helper method that makes the code look more rubyish. Again, maybe we wait until it's needed instead of me assuming it would be a problem ;-)

I don't like/dislike the local method. Did we want clases with only a singleton object? If so, singleton might be what we want and use the instance class method to get at the one and only one instance. https://practicingruby.com/articles/shared/jleygxejeopq

attr_accessor :path

def self.local
Dir.glob('/dev/[hs]d[a-z]').collect do |d|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add "v" to [hsv]d[a-z] because RHEV VirtIO disks show up as vda, vdb, ...

@Fryguy
Copy link
Member

Fryguy commented Jun 25, 2013

@movitto

With regard to mocking/stubbing, I firmly believe the code should not shell out in specs to allow for portability and for allowing the specs to run in different locations whether or not the underlying libraries and such are installed. For example, on TravisCI, there may not be rhn or subscription_manager, if it even runs on a RedHat system at all.

For specs that just need to verify a particular command is executed we can use the .should_receive expectations. For specs that need to return data, the commands/files could be run on a real system with the output captured into files which are mocked in the test. This is not unlike VCR for http mocking, which has proven very useful. If you go through the existing specs we've used this technique in a few places.

The constants question kind of falls out of this testing approach. At one point in the code, we had constants, and then found that for files, a method that returns a value allowed us to easily stub that method with a mocked file. For example, you can see how we stubbed this method with different values for different scenarios by providing different file paths in the spec here. For executable paths, a constant is probably good enough.


Didn't know that about the full paths, and that link is really interesting! We should probably update the rest of the classes with respect to full paths as well.


I think that question mark methods should do the :return_exitstatus => true) == 0 thing for verifying. Most other methods shouldn't be doing an == 0 check at all. The default of the run method is to raise an exception on non-zero exitstatus, which seems correct as it is an exception.


The reason I raised the question about the .local methods is I was thinking about the Singleton pattern (as @jrafanie mentioned although that link seems to make it more confusing/scary than it needs to be). In particular the Singleton instance would be around a method like .local. In fact, the reason we initially had class methods for the other modules is because later we were thinking of Singleton-patterning them.


def unmount
run("/usr/bin/umount",
:params => { nil => [@mount_point] })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these read better as a one-liner.

@coveralls
Copy link

Coverage Status

Coverage remained the same when pulling 52de8bf on movitto:master into d2fba2a on ManageIQ:master.

@movitto
Copy link
Contributor Author

movitto commented Jun 26, 2013

Updated:

  • stub out command invocations in specs

  • lookup command paths from local distro

  • rubyified "service.running?"

  • added 'v' to disks glob

  • more specs and converting existing modules to classes still to come

    I'm not against the singleton pattern but I don't think it applies here. Right now the two classes which define a 'local' method are 'distro' and 'disk'. Obviously disk can't be a singleton but I split distro out into subclasses with accessors in the 'Distros' module for static instances. I feel this is a little bit cleaner and localized approach

Hope everything else looks good.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0%) when pulling dc8f51b on movitto:master into d2fba2a on ManageIQ:master.

@movitto
Copy link
Contributor Author

movitto commented Jun 27, 2013

OK, flushed out the specs / incorporated the remaining feedback. Opened an issue to move the existing modules over to the ood approach. Should be good to go

@chessbyte
Copy link
Member

p = d.partitions
p[0].id.should == 1
p[0].disk.should == d
p[0].size.should == '80.5GB'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't notice this before when there weren't specs, but the callers shouldn't have to deal with a number in this format. We should parse this and return a number, preferably in bytes. Since active_support is already available to this gem, you could probably just get it to call 80.5.gigabytes to get the expected number.

@movitto
Copy link
Contributor Author

movitto commented Jun 28, 2013

The size parsing issue is resolved. The travis failures for the most were due to a difference in environments. I believe I have the fix but can't be certain until travis picks it up and tests this out (is there a more efficient way of doing this?)

@coveralls
Copy link

Coverage Status

Coverage remained the same when pulling 08053e8 on movitto:master into d2fba2a on ManageIQ:master.

@chessbyte
Copy link
Member

@coveralls
Copy link

Coverage Status

Coverage remained the same when pulling 5f2c813 on movitto:master into d2fba2a on ManageIQ:master.


describe "#stop" do
it "stops service" do
@l.should_receive(:run).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@movitto How about @service instead of @l? I had to scroll up to figure out what @l was.

@jrafanie
Copy link
Member

jrafanie commented Jul 1, 2013

@movitto Looks good although this pull request is from your master branch. It's not a huge deal but in the future, it would be helpful to use a somewhat relevant branch name as it becomes part of our history when we merge.

Is this pull longer than the initial pull request? It seems larger than when I originally reviewed it? Were there classes added?

@coveralls
Copy link

Coverage Status

Coverage remained the same when pulling 8e5a243 on movitto:master into d2fba2a on ManageIQ:master.

@movitto
Copy link
Contributor Author

movitto commented Jul 2, 2013

K, made the changes:
s/@l/@service
s/@p/@Partition

Noted about the branch. Will change it w/ new pull requests going forward.

There have been a few revisions, but the architecture largely remains the same. Besides trivial organizational changes, the major differences between the latest revision and the original one are:

  • added generic distro for when distro could not be ascertained
  • abstractify the commands invoked on a per-distro basis
  • standardize command invocation and code syntax/style
  • added specs covering everything
  • small little changes here and there (numeric partition size, test distro, etc)

@movitto
Copy link
Contributor Author

movitto commented Jul 2, 2013

Changed d -> disk , @d -> @disk

@coveralls
Copy link

Coverage Status

Coverage remained the same when pulling ed572ef on movitto:master into d2fba2a on ManageIQ:master.

chessbyte added a commit that referenced this pull request Jul 2, 2013
Add representations of various system entities
@chessbyte chessbyte merged commit dfc0135 into ManageIQ:master Jul 2, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants