Merge pull request #19851 from lujun9972/add-MjAyMDEwMTIgTXkgdG9wIDcga2V5d29yZHMgaW4gUnVzdC5tZAo=

自动选题[tech]: 20201012 My top 7 keywords in Rust
This commit is contained in:
Xingyu.Wang 2020-10-14 21:49:39 +08:00 committed by GitHub
commit a7b4300661
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 241 additions and 0 deletions

View File

@ -0,0 +1,186 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Create an Ansible module for integrating your Google Calendar)
[#]: via: (https://opensource.com/article/20/10/ansible-module-go)
[#]: author: (Nicolas Leiva https://opensource.com/users/nicolas-leiva)
Create an Ansible module for integrating your Google Calendar
======
Learn how to write an Ansible module in Go to integrate Google Calendar
into your automation workflow.
![Business woman on laptop sitting in front of window][1]
In a [previous article][2], I explored how [Ansible][3] can integrate with Google Calendar for change management, but I didn't get into the details of the [Ansible module][4] that was built for this purpose. In this article, I will cover the nuts and bolts of it.
While most [Ansible modules][5]** **are written in [Python][6] (see [this example][7]), that's not the only option you have. You can use other programming languages if you prefer. And if you like [Go][8], this post is for you!
![Gopher illustration][9]
([Maria Letta's Free Gophers Pack][10], [Free Gophers License v1.0][11], modified with permission)
If you are new to Go, check out these [pointers to get started][12].
## Ansible and Go
There are at least four different ways that you can run a Go program from Ansible:
1. Install Go and run your Go code with the `go run` command from Ansible.
2. Cross-compile your Go code for different platforms before execution. Then call the proper binary from Ansible, based on the facts you collect from the host.
3. Run your Go code or compiled binary from a container with the `containers.podman` [collection][13]. Something along the lines of: [code] - name: Run Go container
  podman_container:
    name: go_test_container
    image: golang
    command: go version
    rm: true
    log_options: "path={{ log_file }}"
```
4. Create an [RPM][14] package of your Go code with something like [NFPM][15], and install it in the system of the target host. You can then call it with the Ansible modules [shell][16] or [command][17].
Running an RPM package or container is not Go-specific (cases 3 and 4), so I will focus on the first two options.
## Google Calendar API
You will need to interact with the [Google Calendar API][18], which provides code examples for different programming languages. The one for Go will be the base for your Ansible module.
The tricky part is [enabling the Calendar API][19] and downloading the credentials you generate in the [Google API Console][20] (`Credentials` > `+ CREATE CREDENTIALS` > `OAuth client ID` > `Desktop App`).
![Downloading credentials from Google API Console][21]
(Nicolas Leiva, [CC BY-SA 4.0][22])
The arrow shows where to download your OAuth 2.0 client credentials (JSON file) once you create them in [API Credentials][23].
## Calling the module from Ansible
The `calendar` module takes the `time` to validate as an argument. The example below provides the current time. You can typically get this from [Ansible facts][24] (`ansible_date_time`). The JSON output of the module is registered in a variable named `output` to be used in a subsequent task:
```
\- name: Check if timeslot is taken
  calendar:
    time: "{{ ansible_date_time.iso8601 }}"
  register: output
```
You might wonder where the `calendar` module code lives and how Ansible executes it. Please bear with me for a moment; I'll get to this after I cover other pieces of the puzzle.
## Employ the time logic
With the Calendar API nuances out of the way, you can proceed to interact with the API and build a Go function to capture the module logic. The `time` is taken from the input arguments—in the playbook above—as the initial time (`min`) of the time window to validate (I arbitrarily chose a one-hour duration):
```
func isItBusy(min string) (bool, error) {
        ...
        // max -> min.Add(1 * time.Hour)
        max, err := maxTime(min)
        // ...
        srv, err := calendar.New(client)
        // ...
        freebusyRequest := calendar.FreeBusyRequest{
                TimeMin: min,
                TimeMax: max,
                Items:   []*calendar.FreeBusyRequestItem{&cal},
        }
        // ...
        freebusyRequestResponse, err := freebusyRequestCall.Do()
        // ...
        if len(freebusyRequestResponse.Calendars[name].Busy) == 0 {
                return false, nil
        }
        return true, nil
}
```
It [sends a `FreeBusyRequest`][25] to Google Calendar with the time window's initial and finish time (`min` and `max`). It also creates a calendar [client][26] (`srv`) to authenticate the account correctly using the JSON file with the OAuth 2.0 client credentials. In return, you get a list of events during this time window.
If you get zero events, the function returns `busy=false`. However, if there is at least one event during this time window, it means `busy=true`. You can check out the [full code][27] in my GitHub repository.
## Process the input and creating a response
Now, how does the Go code read the inputs arguments from Ansible and, in turn, generate a response that Ansible can process? The answer to this depends on whether you are running the [Go CLI][28] (command-line interface) or just executing a pre-compiled Go program binary (i.e., options 1 and 2 above).
Both options have their pros and cons. If you use the Go CLI, you can pass the arguments the way you prefer. However, to make it consistent with how it works for binaries you run from Ansible, both alternatives will read a JSON file in the examples presented here.
### Reading the arguments
As shown in the Go code snippet below, an input file is processed, and Ansible provides a path to it when it calls a binary.
The content of the file is unmarshaled into an instance (`moduleArg`) of a previously defined struct (`ModuleArgs`). This is how you tell the Go code which data you expect to receive. This method enables you to gain access to the `time` specified in the playbook via `moduleArg.time`, which is then passed to the time logic function (`isItBusy`) for processing:
```
// ModuleArgs are the module inputs
type ModuleArgs struct {
        Time string
}
func main() {
        ...
        argsFile := os.Args[1]
        text, err := ioutil.ReadFile(argsFile)
        ...
        var moduleArgs ModuleArgs
        err = json.Unmarshal(text, &moduleArgs)
        ...
        busy, err := isItBusy(moduleArg.time)
        ...
}
```
### Generating a response
The values to return are assigned to an instance of a `Response` object. Ansible will need this response includes a couple of boolean flags (`Changed` and `Failed`). You can add any other field you need; in this case, a `Busy` boolean value is carried to signal the response of the time logic function.
The response is marshaled into a message that you print out and Ansible can read:
```
// Response are the values returned from the module
type Response struct {
        Msg     string `json:"msg"`
        Busy    bool   `json:"busy"`
        Changed bool   `json:"changed"`
        Failed  bool   `json:"failed"`
}
func returnResponse(r Response) {
  ...
        response, err = json.Marshal(r)
        ...
        fmt.Println(string(response))
        ...
}
```
You can check out the [full code][29] on GitHub.
## Execute a binary or Go code on the fly?
One of the cool things about Go is that you can cross-compile a Go program to run on different target operating systems and architectures. The binary files you compile can be executed in the target host without installing Go or any dependency.
This flexibility plays nicely with Ansible, which provides the target host details (`ansible_system` and `ansible_architecture`) via Ansible facts. In this example, the target architecture is fixed when compiling (`x86_64`), but binaries for macOS, Linux, and Windows are generated (via `make compile`). This produces the three files that are included in the [`library` folder][30] of the `go_role` role with the form of: `calendar_$system`:
```
 tree roles/go_role/
roles/go_role/
├── library
  ├── calendar_darwin
  ├── calendar_linux
  ├── calendar_windows
  └── go_run
└── tasks
    ├── Darwin.yml
    ├── Go.yml
    ├── Linux.yml
    ├── main.yml
    └── Win32NT.yml
```
The [`go_role` role][31] that packages the `calendar`

View File

@ -0,0 +1,55 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (My top 7 keywords in Rust)
[#]: via: (https://opensource.com/article/20/10/keywords-rust)
[#]: author: (Mike Bursell https://opensource.com/users/mikecamel)
My top 7 keywords in Rust
======
Learn a handful of useful keywords from the Rust standard library.
![Rustacean t-shirt][1]
I've been using [Rust][2] for a few months now, writing rather more of it than I expected—though quite a lot of that has been thrown away as I've learned, improved what I'm writing, and taken some more complex tasks beyond what I originally intended.
I still love it and thought it might be good to talk about some of the important keywords that come up again and again in Rust. I'll provide my personal summary of what they do, why you need to think about how you use them, and anything else that's useful, particularly for people who are new to Rust or coming from another language (such as Java; see my article [_Why I'm enjoying learning Rust as a Java programmer_][3]).
Without further ado, let's get going. A good place for further information is always the official Rust documentation—you'll probably want to start with the [std library][4].
1. **const**  You get to declare constants with const, and you should. This isn't rocket science, but do declare with const, and if you're going to use constants across different modules, then do the right thing and create a `lib.rs` file (the Rust default) into which you can put all of them with a nicely named module. I've had clashes of const variable names (and values!) across different files in different modules simply because I was too lazy to do anything other than cut and paste across files when I could have saved myself lots of work simply by creating a shared module.
2. **let**  You don't _always_ need to declare a variable with a let statement, but your code will be clearer when you do. What's more, always add the type if you can. Rust will do its very best to guess what it should be, but it may not always be able to do so at runtime (in which case [Cargo][5], the compiler, will tell you), or it may even not do what you expect. In the latter case, it's always simpler for Cargo to complain that the function you're assigning from (for instance) doesn't match the declaration than for Rust to try to help you do the wrong thing, only for you to have to spend ages debugging elsewhere.
3. **match**  match was new to me, and I love it. It's not dissimilar to "switch" in other languages but is used extensively in Rust. It makes for legible code, and Cargo will have a good go at warning you if you do something foolish (such as miss possible cases). My general rule of thumb, where I'm managing different options or doing branching, is to ask whether I can use match. If I can, I will.
4. **mut**  When declaring a variable, if it's going to change after its initialisation, then you need to declare it mutable. A common mistake is to declare something mutable when it _isn't_ changed—but the compiler will warn you about that. If you get a warning from Cargo that a mutable variable isn't changed when you think it _is_, then you may wish to check the scope of the variable and make sure that you're using the right version.
5. **return**  I actually very rarely use return, which is for returning a value from a function, because it's usually simpler and clearer to read if you just provide the value (or the function providing the return value) at the end of the function as the last line. Warning: you _will_ forget to omit the semicolon at the end of this line on many occasions; if you do, the compiler won't be happy.
6. **unsafe**  This does what it says on the tin: If you want to do things where Rust can't guarantee memory safety, then you're going to need to use this keyword. I have absolutely no intention of declaring any of my Rust code unsafe now or at any point in the future; one of the reasons Rust is so friendly is because it stops this sort of hackery. If you really need to do this, think again, think yet again, and then redesign. Unless you're a seriously low-level systems programmer, _avoid_ unsafe.
7. **use**  When you want to use an item, e.g., struct, variable, function, etc. from another crate, then you need to declare it at the beginning of the block where you'll be using it. Another common mistake is to do this but fail to add the crate (preferably with a minimum version number) to the `Cargo.toml` file.
This isn't the most complicated article I've ever written, I know, but it's the sort of article I would have appreciated when I was starting to learn Rust. I plan to create similar articles on key functions and other Rust must-knows: let me know if you have any requests!
* * *
_This article was originally published on [Alice, Eve, and Bob][6] and is reprinted with the author's permission._
--------------------------------------------------------------------------------
via: https://opensource.com/article/20/10/keywords-rust
作者:[Mike Bursell][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/mikecamel
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/rustacean-tshirt.jpg?itok=u7LBmyaj (Rustacean t-shirt)
[2]: https://www.rust-lang.org/
[3]: https://opensource.com/article/20/5/rust-java
[4]: https://doc.rust-lang.org/std/
[5]: https://doc.rust-lang.org/cargo/
[6]: https://aliceevebob.com/2020/09/01/rust-my-top-7-keywords/