One place for hosting & domains

      Functions

      Exploring Async/Await Functions in JavaScript


      Introduction

      Promises give us an easier way to deal with asynchrony in our code in a sequential manner. Considering that our brains are not designed to deal with asynchronicity efficiently, this is a much welcome addition. Async/await functions, a new addition with ES2017 (ES8), help us even more in allowing us to write completely synchronous-looking code while performing asynchronous tasks behind the scenes.

      The functionality achieved using async functions can be recreated by combining promises with generators, but async functions give us what we need without any extra boilerplate code.

      Simple Example

      In the following example, we first declare a function that returns a promise that resolves to a value of 🤡 after 2 seconds. We then declare an async function and await for the promise to resolve before logging the message to the console:

      function scaryClown() {
        return new Promise(resolve => {
          setTimeout(() => {
            resolve('🤡');
          }, 2000);
        });
      }
      
      async function msg() {
        const msg = await scaryClown();
        console.log('Message:', msg);
      }
      
      msg(); // Message: 🤡 <-- after 2 seconds
      

      await is a new operator used to wait for a promise to resolve or reject. It can only be used inside an async function.

      The power of async functions becomes more evident when there are multiple steps involved:

      function who() {
        return new Promise(resolve => {
          setTimeout(() => {
            resolve('🤡');
          }, 200);
        });
      }
      
      function what() {
        return new Promise(resolve => {
          setTimeout(() => {
            resolve('lurks');
          }, 300);
        });
      }
      
      function where() {
        return new Promise(resolve => {
          setTimeout(() => {
            resolve('in the shadows');
          }, 500);
        });
      }
      
      async function msg() {
        const a = await who();
        const b = await what();
        const c = await where();
      
        console.log(`${ a } ${ b } ${ c }`);
      }
      
      msg(); // 🤡 lurks in the shadows <-- after 1 second
      

      A word of caution however, in the above example each step is done sequentially, with each additional step waiting for the step before to resolve or reject before continuing. If you instead want the steps to happen in parallel, you can simply use Promise.all to wait for all the promises to have fulfilled:

      // ...
      
      async function msg() {
        const [a, b, c] = await Promise.all([who(), what(), where()]);
      
        console.log(`${ a } ${ b } ${ c }`);
      }
      
      msg(); // 🤡 lurks in the shadows <-- after 500ms
      

      Promise.all returns an array with the resolved values once all the passed-in promises have resolved.

      In the above we also make use of some nice array destructuring to make our code succinct.

      Promise-Returning

      Async functions always return a promise, so the following may not produce the result you’re after:

      async function hello() {
        return 'Hello Alligator!';
      }
      
      const b = hello();
      
      console.log(b); // [object Promise] { ... }
      

      Since what’s returned is a promise, you could do something like this instead:

      async function hello() {
        return 'Hello Alligator!';
      }
      
      const b = hello();
      
      b.then(x => console.log(x)); // Hello Alligator!
      

      …or just this:

      async function hello() {
        return 'Hello Alligator!';
      }
      
      hello().then(x => console.log(x)); // Hello Alligator!
      

      Different Forms

      So far with our examples we saw the async function as a function declaration, but you we can also define async function expressions and async arrow functions:

      Async Function Expression

      Here’s the async function from our first example, but defined as a function expression:

      const msg = async function() {
        const msg = await scaryClown();
        console.log('Message:', msg);
      }
      

      Async Arrow Function

      Here’s that same example once again, but this time defined as an arrow function:

      const msg = async () => {
        const msg = await scaryClown();
        console.log('Message:', msg);
      }
      

      Error Handling

      Something else that’s very nice about async functions is that error handling is also done completely synchronously, using good old try…catch statements. Let’s demonstrate by using a promise that will reject half the time:

      function yayOrNay() {
        return new Promise((resolve, reject) => {
          const val = Math.round(Math.random() * 1); // 0 or 1, at random
      
          val ? resolve('Lucky!!') : reject('Nope 😠');
        });
      }
      
      async function msg() {
        try {
          const msg = await yayOrNay();
          console.log(msg);
        } catch(err) {
          console.log(err);
        }
      }
      
      msg(); // Lucky!!
      msg(); // Lucky!!
      msg(); // Lucky!!
      msg(); // Nope 😠
      msg(); // Lucky!!
      msg(); // Nope 😠
      msg(); // Nope 😠
      msg(); // Nope 😠
      msg(); // Nope 😠
      msg(); // Lucky!!
      

      Given that async functions always return a promise, you can also deal with unhandled errors as you would normally using a catch statement:

      async function msg() {
        const msg = await yayOrNay();
        console.log(msg);
      }
      
      msg().catch(x => console.log(x));
      

      This synchronous error handling doesn’t just work when a promise is rejected, but also when there’s an actual runtime or syntax error happening. In the following example, the second time with call our msg function we pass in a number value that doesn’t have a toUpperCase method in its prototype chain. Our try…catch block catches that error just as well:

      function caserUpper(val) {
        return new Promise((resolve, reject) => {
          resolve(val.toUpperCase());
        });
      }
      
      async function msg(x) {
        try {
          const msg = await caserUpper(x);
          console.log(msg);
        } catch(err) {
          console.log('Ohh no:', err.message);
        }
      }
      
      msg('Hello'); // HELLO
      msg(34); // Ohh no: val.toUpperCase is not a function
      

      Async Functions With Promise-Based APIS

      As we showed in our primer to the Fetch API, web APIs that are promise-based are a perfect candidate for async functions:

      async function fetchUsers(endpoint) {
        const res = await fetch(endpoint);
        let data = await res.json();
      
        data = data.map(user => user.username);
      
        console.log(data);
      }
      
      fetchUsers('https://jsonplaceholder.typicode.com/users');
      // ["Bret", "Antonette", "Samantha", "Karianne", "Kamren", "Leopoldo_Corkery", "Elwyn.Skiles", "Maxime_Nienow", "Delphine", "Moriah.Stanton"]
      

      <%>[note]
      Browser Support:
      As of 2020, 94% of browsers worldwide can handle async/await in javascript Notable exceptions are IE11 and Opera Mini.

      Conclusion

      Before Async/await functions, JavaScript code that relied on lots of asynchronous events (for example: code that made lots of calls to APIs) would end up in what some called “callback hell” – A chain of functions and callbacks that was very difficult to read and understand.

      Async and await allow us to write asynchronous JavaScript code that reads much more clearly.



      Source link

      Understanding Arrow Functions in JavaScript


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      The 2015 edition of the ECMAScript specification (ES6) added arrow function expressions to the JavaScript language. Arrow functions are a new way to write anonymous function expressions, and are similar to lambda functions in some other programming languages, such as Python.

      Arrow functions differ from traditional functions in a number of ways, including the way their scope is determined and how their syntax is expressed. Because of this, arrow functions are particularly useful when passing a function as a parameter to a higher-order function, such as when you are looping over an array with built-in iterator methods. Their syntactic abbreviation can also allow you to improve the readability of your code.

      In this article, you will review function declarations and expressions, learn about the differences between traditional function expressions and arrow function expressions, learn about lexical scope as it pertains to arrow functions, and explore some of the syntactic shorthand permitted with arrow functions.

      Defining Functions

      Before delving into the specifics of arrow function expressions, this tutorial will briefly review traditional JavaScript functions in order to better show the unique aspects of arrow functions later on.

      The How To Define Functions in JavaScript tutorial earlier in this series introduced the concept of function declarations and function expressions. A function declaration is a named function written with the function keyword. Function declarations load into the execution context before any code runs. This is known as hoisting, meaning you can use the function before you declare it.

      Here is an example of a sum function that returns the sum of two parameters:

      function sum(a, b) {
        return a + b
      }
      

      You can execute the sum function before declaring the function due to hoisting:

      sum(1, 2)
      
      function sum(a, b) {
        return a + b
      }
      

      Running this code would give the following output:

      Output

      3

      You can find the name of the function by logging the function itself:

      console.log(sum)
      

      This will return the function, along with its name:

      Output

      ƒ sum(a, b) { return a + b }

      A function expression is a function that is not pre-loaded into the execution context, and only runs when the code encounters it. Function expressions are usually assigned to a variable, and can be anonymous, meaning the function has no name.

      In this example, write the same sum function as an anonymous function expression:

      const sum = function (a, b) {
        return a + b
      }
      

      You’ve now assigned the anonymous function to the sum constant. Attempting to execute the function before it is declared will result in an error:

      sum(1, 2)
      
      const sum = function (a, b) {
        return a + b
      }
      

      Running this will give:

      Output

      Uncaught ReferenceError: Cannot access 'sum' before initialization

      Also, note that the function does not have a named identifier. To illustrate this, write the same anonymous function assigned to sum, then log sum to the console:

      const sum = function (a, b) {
        return a + b
      }
      
      console.log(sum)
      

      This will show you the following:

      Output

      ƒ (a, b) { return a + b }

      The value of sum is an anonymous function, not a named function.

      You can name function expressions written with the function keyword, but this is not popular in practice. One reason you might want to name a function expression is to make error stack traces easier to debug.

      Consider the following function, which uses an if statement to throw an error if the function parameters are missing:

      const sum = function namedSumFunction(a, b) {
        if (!a || !b) throw new Error('Parameters are required.')
      
        return a + b
      }
      
      sum();
      

      The highlighted section gives the function a name, and then the function uses the or || operator to throw an error object if either of the parameters is missing.

      Running this code will give you the following:

      Output

      Uncaught Error: Parameters are required. at namedSumFunction (<anonymous>:3:23) at <anonymous>:1:1

      In this case, naming the function gives you a quick idea of where the error is.

      An arrow function expression is an anonymous function expression written with the “fat arrow” syntax (=>).

      Rewrite the sum function with arrow function syntax:

      const sum = (a, b) => {
        return a + b
      }
      

      Like traditional function expressions, arrow functions are not hoisted, and so you cannot call them before you declare them. They are also always anonymous—there is no way to name an arrow function. In the next section, you will explore more of the syntactical and practical differences between arrow functions and traditional functions.

      Arrow Function Behavior and Syntax

      Arrow functions have a few important distinctions in how they work that distinguish them from traditional functions, as well as a few syntactic enhancements. The biggest functional differences are that arrow functions do not have their own this binding or prototype and cannot be used as a constructor. Arrow functions can also be written as a more compact alternative to traditional functions, as they grant the ability to omit parentheses around parameters and add the concept of a concise function body with implicit return.

      In this section, you will go through examples that illustrate each of these cases.

      Lexical this

      The keyword this is often considered a tricky topic in JavaScript. The article Understanding This, Bind, Call, and Apply in JavaScript explains how this works, and how this can be implicitly inferred based on whether the program uses it in the global context, as a method within an object, as a constructor on a function or class, or as a DOM event handler.

      Arrow functions have lexical this, meaning the value of this is determined by the surrounding scope (the lexical environment).

      The next example will demonstrate the difference between how traditional and arrow functions handle this. In the following printNumbers object, there are two properties: phrase and numbers. There is also a method on the object, loop, which should print the phrase string and the current value in numbers:

      const printNumbers = {
        phrase: 'The current value is:',
        numbers: [1, 2, 3, 4],
      
        loop() {
          this.numbers.forEach(function (number) {
            console.log(this.phrase, number)
          })
        },
      }
      

      One might expect the loop function to print the string and current number in the loop on each iteraton. However, in the result of running the function the phrase is actually undefined:

      printNumbers.loop()
      

      This will give the following:

      Output

      undefined 1 undefined 2 undefined 3 undefined 4

      As this shows, this.phrase is undefined, indicating that this within the anonymous function passed into the forEach method does not refer to the printNumbers object. This is because a traditional function will not determine its this value from the scope of the environment, which is the printNumbers object.

      In older versions of JavaScript, you would have had to use the bind method, which explicitly sets this. This pattern can be found often in some earlier versions of frameworks, like React, before the advent of ES6.

      Use bind to fix the function:

      const printNumbers = {
        phrase: 'The current value is:',
        numbers: [1, 2, 3, 4],
      
        loop() {
          // Bind the `this` from printNumbers to the inner forEach function
          this.numbers.forEach(
            function (number) {
              console.log(this.phrase, number)
            }.bind(this),
          )
        },
      }
      
      printNumbers.loop()
      

      This will give the expected result:

      Output

      The current value is: 1 The current value is: 2 The current value is: 3 The current value is: 4

      Arrow functions provide a more direct way of dealing with this. Since their this value is determined based on the lexical scope, the inner function called in forEach can now access the properties of the outer printNumbers object, as demonstrated:

      const printNumbers = {
        phrase: 'The current value is:',
        numbers: [1, 2, 3, 4],
      
        loop() {
          this.numbers.forEach((number) => {
            console.log(this.phrase, number)
          })
        },
      }
      
      printNumbers.loop()
      

      This will give the expected result:

      Output

      The current value is: 1 The current value is: 2 The current value is: 3 The current value is: 4

      These examples establish that using arrow functions in built-in array methods like forEach, map, filter, and reduce can be more intuitive and easier to read, making this strategy more likely to fulfill expectations.

      Arrow Functions as Object Methods

      While arrow functions are excellent as parameter functions passed into array methods, they are not effective as object methods because of the way they use lexical scoping for this. Using the same example as before, take the loop method and turn it into an arrow function to discover how it will execute:

      const printNumbers = {
        phrase: 'The current value is:',
        numbers: [1, 2, 3, 4],
      
        loop: () => {
          this.numbers.forEach((number) => {
            console.log(this.phrase, number)
          })
        },
      }
      

      In this case of an object method, this should refer to properties and methods of the printNumbers object. However, since an object does not create a new lexical scope, an arrow function will look beyond the object for the value of this.

      Call the loop() method:

      printNumbers.loop()
      

      This will give the following:

      Output

      Uncaught TypeError: Cannot read property 'forEach' of undefined

      Since the object does not create a lexical scope, the arrow function method looks for this in the outer scope–Window in this example. Since the numbers property does not exist on the Window object, it throws an error. As a general rule, it is safer to use traditional functions as object methods by default.

      Arrow Functions Have No constructor or prototype

      The Understanding Prototypes and Inheritance in JavaScript tutorial earlier in this series explained that functions and classes have a prototype property, which is what JavaScript uses as a blueprint for cloning and inheritance.

      To illustrate this, create a function and log the the automatically assigned prototype property:

      function myFunction() {
        this.value = 5
      }
      
      // Log the prototype property of myFunction
      console.log(myFunction.prototype)
      

      This will print the following to the console:

      Output

      {constructor: ƒ}

      This shows that in the prototype property there is an object with a constructor. This allows you to use the new keyword to create an instance of the function:

      const instance = new myFunction()
      
      console.log(instance.value)
      

      This will yield the value of the value property that you defined when you first declared the function:

      Output

      5

      In contrast, arrow functions do not have a prototype property. Create a new arrow function and try to log its prototype:

      const myArrowFunction = () => {}
      
      // Attempt to log the prototype property of myArrowFunction
      console.log(myArrowFunction.prototype)
      

      This will give the following:

      Output

      undefined

      As a result of the missing prototype property, the new keyword is not available and you cannot construct an instance from the arrow function:

      const arrowInstance = new myArrowFunction()
      
      console.log(arrowInstance)
      

      This will give the following error:

      Output

      Uncaught TypeError: myArrowFunction is not a constructor

      This is consistent with our earlier example: Since arrow functions do not have their own this value, it follows that you would be unable to use an arrow function as a constructor.

      As shown here, arrow functions have a lot of subtle changes that make them operate differently from traditional functions in ES5 and earlier. There have also been a few optional syntactical changes that make writing arrow functions quicker and less verbose. The next section will show examples of these syntax changes.

      Implicit Return

      The body of a traditional function is contained within a block using curly brackets {} and ends when the code encounters a return keyword. The following is what this implementation looks like as an arrow function:

      const sum = (a, b) => {
        return a + b
      }
      

      Arrow functions introduce concise body syntax, or implicit return. This allows the omission of the curly brackets and the return keyword.

      const sum = (a, b) => a + b
      

      Implicit return is useful for creating succinct one-line operations in map, filter, and other common array methods. Note that both the brackets and the return keyword must be omitted. If you cannot write the body as a one-line return statement, then you will have to use the normal block body syntax.

      In the case of returning an object, syntax requires that you wrap the object literal in parentheses. Otherwise, the brackets will be treated as a function body and will not compute a return value.

      To illustrate this, find the following example:

      const sum = (a, b) => ({result: a + b})
      
      sum(1, 2)
      

      This will give the following output:

      Output

      {result: 3}

      Omitting Parentheses Around a Single Parameter

      Another useful syntactic enhancement is the ability to remove parentheses from around a single parameter in a function. In the following example, the square function only operates on one parameter, x:

      const square = (x) => x * x
      

      As a result, you can omit the parentheses around the parameter, and it will work just the same:

      const square = x => x * x
      
      square(10)
      

      This will give the following:

      Output

      100

      Note that if a function takes no parameters, parentheses will be required:

      const greet = () => 'Hello!'
      
      greet()
      

      Calling greet() will work as follows:

      Output

      'Hello!'

      Some codebases choose to omit parentheses wherever possible, and others choose to always keep parentheses around parameters no matter what, particularly in codebases that use TypeScript and require more information about each variable and parameter. When deciding how to write your arrow functions, check the style guide of the project to which you are contributing.

      Conclusion

      In this article, you reviewed traditional functions and the difference between function declarations and function expressions. You learned that arrow functions are always anonymous, do not have a prototype or constructor, cannot be used with the new keyword, and determine the value of this through lexical scope. Finally, you explored the new syntactic enhancements available to arrow functions, such as implicit return and parentheses omission for single parameter functions.

      For a review of basic functions, read How To Define Functions in JavaScript. To read more about the concept of scope and hoisting in JavaScript, read Understanding Variables, Scope, and Hoisting in JavaScript.



      Source link

      How To Run Serverless Functions Using OpenFaaS on DigitalOcean Kubernetes


      The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Typically, hosting a software application on the internet requires infrastructure management, planning, and monitoring for a monolithic system. Unlike this traditional approach, the serverless architecture (also known as function as a service, or FaaS) breaks down your application into functions. These functions are stateless, self contained, event triggered, functionally complete entities that communicate via APIs that you manage, instead of the underlying hardware and explicit infrastructure provisioning. Functions are scalable by design, portable, faster to set up and easier to test than ordinary apps. For the serverless architecture to work in principle, it requires a platform agnostic way of packaging and orchestrating functions.

      OpenFaaS is an open-source framework for implementing the serverless architecture on Kubernetes, using Docker containers for storing and running functions. It allows any program to be packaged as a container and managed as a function via the command line or the integrated web UI. OpenFaaS has excellent support for metrics and provides autoscaling for functions when demand increases.

      In this tutorial, you will deploy OpenFaaS to your DigitalOcean Kubernetes cluster at your domain and secure it using free Let’s Encrypt TLS certificates. You’ll also explore its web UI and deploy existing and new functions using the faas-cli, the official command line tool. In the end, you’ll have a flexible system for deploying serverless functions in place.

      Prerequisites

      • A DigitalOcean Kubernetes cluster with your connection configured as the kubectl default. The cluster must have at least 8GB RAM and 4 CPU cores available for OpenFaaS (more will be required in case of heavier use). Instructions on how to configure kubectl are shown under the Connect to your Cluster step when you create your cluster. To create a Kubernetes cluster on DigitalOcean, see Kubernetes Quickstart.
      • Docker installed on your local machine. Following Steps 1 and 2 for your distribution, see How To Install Docker.
      • An account at Docker Hub for storing Docker images you’ll create during this tutorial.
      • faas-cli, the official CLI tool for managing OpenFaaS, installed on your local machine. For instructions for multiple platforms, visit the official docs.
      • The Helm package manager installed on your local machine. To do this, complete Step 1 and add the stable repo from Step 2 of the How To Install Software on Kubernetes Clusters with the Helm 3 Package Manager tutorial.
      • The Nginx Ingress Controller and Cert-Manager installed on your cluster using Helm in order to expose OpenFaaS using Ingress Resources. For guidance, follow How to Set Up an Nginx Ingress on DigitalOcean Kubernetes Using Helm.
      • A fully registered domain name to host OpenFaaS, pointed at the Load Balancer used by the Nginx Ingress. This tutorial will use openfaas.your_domain throughout. You can purchase a domain name on Namecheap, get one for free on Freenom, or use the domain registrar of your choice.

      Note: The domain name you use in this tutorial must differ from the one used in the “How To Set Up an Nginx Ingress on DigitalOcean Kubernetes” prerequisite tutorial.

      Step 1 — Installing OpenFaaS using Helm

      In this step, you will install OpenFaaS to your Kubernetes cluster using Helm and expose it at your domain.

      As part of the Nginx Ingress Controller prerequisite, you created example Services and an Ingress. You won’t need them in this tutorial, so you can delete them by running the following commands:

      • kubectl delete -f hello-kubernetes-first.yaml
      • kubectl delete -f hello-kubernetes-second.yaml
      • kubectl delete -f hello-kubernetes-ingress.yaml

      Since you’ll be deploying functions as Kubernetes objects, it’s helpful to store them and OpenFaaS itself in separate namespaces in your cluster. The OpenFaaS namespace will be called openfaas, and the functions namespace will be openfaas-fn. Create them in your cluster by running the following command:

      • kubectl apply -f https://raw.githubusercontent.com/openfaas/faas-netes/master/namespaces.yml

      You’ll see the following output:

      Output

      namespace/openfaas created namespace/openfaas-fn created

      Next, you’ll need to add the OpenFaaS Helm repository, which hosts the OpenFaaS chart. To do this, run the following command:

      • helm repo add openfaas https://openfaas.github.io/faas-netes/

      Helm will display the following output:

      Output

      "openfaas" has been added to your repositories

      Refresh Helm’s chart cache:

      You’ll see the following output:

      Output

      Hang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "openfaas" chart repository ...Successfully got an update from the "jetstack" chart repository ...Successfully got an update from the "stable" chart repository Update Complete. ⎈ Happy Helming!⎈

      Before installing OpenFaaS, you’ll need to customize some chart parameters. You’ll store them on your local machine, in a file named values.yaml. Create and open the file with your text editor:

      Add the following lines:

      values.yaml

      functionNamespace: openfaas-fn
      generateBasicAuth: true
      
      ingress:
        enabled: true
        annotations:
          kubernetes.io/ingress.class: "nginx"
        hosts:
          - host: openfaas.your_domain
            serviceName: gateway
            servicePort: 8080
            path: /
      

      First, you specify the namespace where functions will be stored by assigning openfaas-fn to the functionNamespace variable. By setting generateBasicAuth to true, you order Helm to set up mandatory authentication when accessing the OpenFaaS web UI and to generate an admin username and password login combination for you.

      Then, you enable Ingress creation and further configure it to use the Nginx Ingress Controller and serve the gateway OpenFaaS service at your domain.

      Remember to replace openfaas.your_domain with your desired domain from the prerequisites. When you are done, save and close the file.

      Finally, install OpenFaaS into the openfaas namespace with the customized values:

      • helm upgrade openfaas --install openfaas/openfaas --namespace openfaas -f values.yaml

      You will see the following output:

      Output

      Release "openfaas" does not exist. Installing it now. NAME: openfaas LAST DEPLOYED: ... NAMESPACE: openfaas STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: To verify that openfaas has started, run: kubectl -n openfaas get deployments -l "release=openfaas, app=openfaas" To retrieve the admin password, run: echo $(kubectl -n openfaas get secret basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode)

      The output shows that the installation was successful. Run the following command to reveal the password for the admin account:

      • echo $(kubectl -n openfaas get secret basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode) | tee openfaas-password.txt

      The decoded password is written to the output and to a file called openfaas-password.txt at the same time using tee. Note the output, which is your OpenFaaS password for the admin account.

      You can watch OpenFaaS containers become available by running the following command:

      • kubectl -n openfaas get deployments -l "release=openfaas, app=openfaas"

      When all listed deployments become ready, type CTRL + C to exit.

      You can now navigate to the specified domain in your web browser. Input admin as the username and the accompanying password when prompted. You’ll see the OpenFaaS web UI:

      OpenFaaS - Empty Control Panel

      You’ve successfully installed OpenFaaS and exposed its control panel at your domain. Next, you’ll secure it using free TLS certificates from Let’s Encrypt.

      Step 2 — Enabling TLS for Your Domain

      In this step, you’ll secure your exposed domain using Let’s Encrypt certificates, provided by cert-manager.

      To do this, you’ll need to edit the ingress config in values.yaml. Open it for editing:

      Add the highlighted lines:

      values.yaml

      generateBasicAuth: true
      
      ingress:
        enabled: true
        annotations:
          kubernetes.io/ingress.class: "nginx"
          cert-manager.io/cluster-issuer: letsencrypt-prod
        tls:
          - hosts:
              - openfaas.your_domain
            secretName: openfaas-crt
        hosts:
          - host: openfaas.your_domain
            serviceName: gateway
            servicePort: 8080
            path: /
      

      The tls block defines in what Secret the certificates for your sites (listed under hosts) will store their certificates, which the letsencrypt-prod ClusterIssuer issues. Generally, the specified Secret must be different for every Ingress in your cluster.

      Remember to replace openfaas.your_domain with your desired domain, then save and close the file.

      Apply the changes to your cluster by running the following command:

      • helm upgrade openfaas --install openfaas/openfaas --namespace openfaas -f values.yaml

      You’ll see the following output:

      Output

      Release "openfaas" has been upgraded. Happy Helming! NAME: openfaas LAST DEPLOYED: ... NAMESPACE: openfaas STATUS: deployed REVISION: 2 TEST SUITE: None NOTES: To verify that openfaas has started, run: kubectl -n openfaas get deployments -l "release=openfaas, app=openfaas" To retrieve the admin password, run: echo $(kubectl -n openfaas get secret basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode)

      You’ll need to wait a few minutes for the Let’s Encrypt servers to issue a certificate for your domain. In the meantime, you can track its progress by inspecting the output of the following command:

      • kubectl describe certificate openfaas-crt -n openfaas

      The end of the output will look similar to this:

      Output

      Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal GeneratedKey 24m cert-manager Generated a new private key Normal Requested 16m cert-manager Created new CertificateRequest resource "openfaas-crt-1017759607" Normal Issued 16m cert-manager Certificate issued successfully

      When the last line of output reads Certificate issued successfully, you can exit by pressing CTRL + C. Refresh your domain in your browser to test. You’ll see the padlock to the left of the address bar in your browser, signifying that your connection is secure.

      You’ve secured your OpenFaaS domain using free TLS certificates from Let’s Encrypt. Now you’ll use the web UI and manage functions from it.

      Step 3 — Deploying Functions via the Web UI

      In this section, you’ll explore the OpenFaaS web UI and then deploy, manage, and invoke functions from it.

      The OpenFaaS web UI has two main parts: on the left-hand side, a column where the deployed functions will be listed, and the central panel, where you’ll see detailed info about a selected function and be able to interact with it.

      To deploy a new function, click the Deploy New Function button underneath the OpenFaaS logo on the upper left. You’ll see a dialog asking you to choose a function:

      OpenFaaS - Deploy a New Function dialog

      The FROM STORE tab lists pre-made functions from the official OpenFaaS function store that you can deploy right away. Each function is shown with a short description, and you can select the link icon on the right of a function to take a look at its source code. To deploy a store function from this list, select it, and then click the DEPLOY button.

      You can also supply your own function by switching to the CUSTOM tab:

      OpenFaaS - Deploy a Custom Function

      Here, you’d need to specify a Docker image of your function that is configured specifically for OpenFaaS and available at a Docker registry (such as Docker Hub). In this step, you’ll deploy a pre-made function from the OpenFaaS store, then in the next steps you’ll create and deploy custom functions to Docker Hub.

      You’ll deploy the NodeInfo function, which returns information about the machine it’s deployed on, such as CPU architecture, number of cores, total RAM memory available, and uptime (in seconds).

      From the list of store functions, select NodeInfo and click DEPLOY. It will soon show up in the list of deployed functions.

      OpenFaaS - NodeInfo Deployed

      Select it. In the central part of the screen, you’ll see basic information about the deployed function.

      OpenFaaS - Deployed Function Info

      The status of the function updates in real time, and should quickly turn to Ready. If it stays at Not Ready for longer periods of time, it’s most likely that your cluster lacks the resources to accept a new pod. You can follow How To Resize Droplets for information on how to fix this.

      Once Ready, the deployed function is accessible at the shown URL. To test it, you can navigate to the URL in your browser, or call it from the Invoke function panel located beneath the function info.

      OpenFaaS - Invoke Deployed Function

      You can select between Text, JSON, and Download to indicate the type of response you expect. If you want the request to be a POST instead of GET, you can supply request data in the Request body field.

      To call the nodeinfo function, click the INVOKE button. OpenFaaS will craft and execute a HTTP request according to the selected options and fill in the response fields with received data.

      OpenFaaS - nodeinfo Function Response

      The response status is HTTP 200 OK, which means that the request was executed successfully. The response body contains system information that the NodeInfo function collects, meaning that it’s properly accessible and working correctly.

      To delete a function, select it from the list and click the garbage can icon in the right upper corner of the page. When prompted, click OK to confirm. The function’s status will turn to Not Ready (which means it’s being removed from the cluster) and the function will soon vanish from the UI altogether.

      In this step, you’ve used the OpenFaaS web UI, as well as deploy and manage functions from it. You’ll now see how you can deploy and manage OpenFaaS functions using the command line.

      Step 4 — Managing Functions Using the faas-cli

      In this section, you’ll configure the faas-cli to work with your cluster. Then, you’ll deploy and manage your existing functions through the command line.

      To avoid having to specify your OpenFaaS domain every time you run the faas-cli, you’ll store it in an environment variable called OPENFAAS_URL, whose value the faas-cli will automatically pick up and use during execution.

      Open .bash_profile in your home directory for editing:

      Add the following line:

      ~/.bash_profile

      . . .
      export OPENFAAS_URL=https://openfaas.your_domain
      

      Remember to replace openfaas.your_domain with your domain, then save and close the file.

      To avoid having to log in again, manually evaluate the file:

      Now, ensure that you have faas-cli installed on your local machine. If you haven’t yet installed it, do so by following the instructions outlined in the official docs.

      Then, set up your login credentials by running the following command:

      • cat ~/openfaas-password.txt | faas-cli login --username admin --password-stdin

      The output will look like:

      Output

      Calling the OpenFaaS server to validate the credentials... credentials saved for admin https://openfaas.your_domain

      To deploy a function from the store, run the following command:

      • faas store deploy function_name

      You can try deploying nodeinfo by running:

      • faas store deploy nodeinfo

      You’ll see output like the following:

      Output

      Deployed. 202 Accepted. URL: https://openfaas.your_domain/function/nodeinfo

      To list deployed functions, run faas list:

      Your existing functions will be shown:

      Output

      Function Invocations Replicas nodeinfo 0 1

      To get detailed info about a deployed function, use faas describe:

      The output will be similar to:

      Name:                nodeinfo
      Status:              Ready
      Replicas:            1
      Available replicas:  1
      Invocations:         0
      Image:               functions/nodeinfo-http:latest
      Function process:
      URL:                 https://openfaas.your_domain/function/nodeinfo
      Async URL:           https://openfaas.your_domain/async-function/nodeinfo
      Labels:              faas_function : nodeinfo
                           uid : 514253614
      Annotations:         prometheus.io.scrape : false
      

      You can invoke a function with faas invoke:

      You’ll get the following message:

      Output

      Reading from STDIN - hit (Control + D) to stop.

      You can then provide a request body. If you do, the method will be POST instead of GET. When you are done with inputting data, or want the request to be GET, press CTRL + D. The faas-cli will then execute the inferred request and output the response, similarly to the web UI.

      To delete a function, run faas remove:

      You’ll get the following output:

      Output

      Deleting: nodeinfo. Removing old function.

      Run faas list again to see that nodeinfo was removed:

      Output

      Function Invocations Replicas

      In this step, you’ve deployed, listed, invoked, and removed functions in your cluster from the command line using the faas-cli. In the next step, you’ll create your own function and deploy it to your cluster.

      Step 5 — Creating and Deploying a New Function

      Now you’ll create a sample Node.JS function using the faas-cli and deploy it to your cluster.

      The resulting function you’ll create will be packaged as a Docker container and published on Docker Hub. To be able to publish containers, you’ll need to log in by running the following command:

      Enter your Docker Hub username and password when prompted to finish the login process.

      You’ll store the sample Node.JS function in a folder named sample-js-function. Create it using the following command:

      Navigate to it:

      Populate the directory with the template of a JS function by running the following command:

      • faas new sample-js --lang node

      The output will look like this:

      Output

      2020/03/24 17:06:08 No templates found in current directory. 2020/03/24 17:06:08 Attempting to expand templates from https://github.com/openfaas/templates.git 2020/03/24 17:06:10 Fetched 19 template(s) : [csharp csharp-armhf dockerfile go go-armhf java11 java11-vert -x java8 node node-arm64 node-armhf node12 php7 python python-armhf python3 python3-armhf python3-debian ru by] from https://github.com/openfaas/templates.git Folder: sample-js created. ___ _____ ____ / _ _ __ ___ _ __ | ___|_ _ __ _/ ___| | | | | '_ / _ '_ | |_ / _` |/ _` ___ | |_| | |_) | __/ | | | _| (_| | (_| |___) | ___/| .__/ ___|_| |_|_| __,_|__,_|____/ |_| Function created in folder: sample-js Stack file written: sample-js.yml ...

      As written in the output, the code for the function itself is in the folder sample-js, while the OpenFaaS configuration for the function is in the file sample-js.yaml. Under the sample-js directory (which resembles a regular Node.JS project) are two files, handler.js and package.json.

      handler.js contains actual JS code that will return a response when the function is called. The contents of the handler look like the following:

      sample-js-function/sample-js/handler.js

      "use strict"
      
      module.exports = async (context, callback) => {
          return {status: "done"}
      }
      

      It exports a lambda function with two parameters, a context with request data and a callback that you can use to pass back response data, instead of just returning it.

      Open this file for editing:

      • nano sample-js/handler.js

      Change the highlighted line as follows:

      sample-js-function/sample-js/handler.js

      "use strict"
      
      module.exports = async (context, callback) => {
          return {status: "<h1>Hello Sammy!</h1>"}
      }
      

      When you are done, save and close the file. This OpenFaaS function will, when called, write Hello Sammy! to the response.

      Next, open the configuration file for editing:

      It will look like the following:

      sample-js-function/sample-js.yml

      version: 1.0
      provider:
        name: openfaas
        gateway: https://openfaas.your_domain
      functions:
        sample-js:
          lang: node
          handler: ./sample-js
          image: sample-js:latest
      

      For the provider, it specifies openfaas and a default gateway. Then, it defines the sample-js function, specifies its language (node), its handler and the Docker image name, which you’ll need to modify to include your Docker Hub account username, like so:

      sample-js-function/sample-js.yml

      version: 1.0
      provider:
        name: openfaas
        gateway: http://127.0.0.1:8080
      functions:
        sample-js:
          lang: node
          handler: ./sample-js
          image: your_docker_hub_username/sample-js:latest
      

      Save and close the file.

      Then, build the Docker image, push it to Docker Hub, and deploy it on your cluster, all at the same time by running the following command:

      There will be a lot of output (mainly from Docker), which will end like this:

      Output

      . . . [0] < Pushing sample-js [your_docker_hub_username/sample-js:latest] done. [0] Worker done. Deploying: sample-js. Deployed. 202 Accepted. URL: https://openfaas.your_domain/function/sample-js

      Invoke your newly deployed function to make sure it’s working:

      Press CTRL + D. You’ll see the following output:

      Output

      <h1>Hello Sammy!</h1>

      This means that the function was packaged and deployed correctly.

      You can remove the function by running:

      You have now successfully created and deployed a custom Node.JS function on your OpenFaaS instance in your cluster.

      Conclusion

      You’ve deployed OpenFaaS on your DigitalOcean Kubernetes cluster and are ready to deploy and access both pre-made and custom functions. Now, you are able to implement the Function as a Service architecture, which can increase resource utilization and bring performance improvements to your apps.

      If you’d like to learn more about advanced OpenFaaS features, such as autoscaling for your deployed functions and monitoring their performance, visit the official docs.



      Source link