Tuesday, March 26, 2024

Difference between cluster, pod, container and node in Kubernete

Container is a place that a program can be deployed and executed, it has all the dependent runtime libraries. Docker is a good candidate to work as a container in order to hold a program and run from there. In jenkensfile, container is specified under containers section, each container in pad is specified with containerTemplate method. 

Pod contains one or more containers. Pods are smallest deployable unit in Kubernetes, all containers within a pod share the storage, network resource. In jenkinsfile, pod is represented with podTemplate method.

Cluster holds and manages pods, the pods within a cluster can be grouped based on namespace.

Node means the computer machines, either physical or virtual machines, that host the pods. In jenkins file, node is specified with node method, the label parameter specifies the node machine's name.

Monday, March 25, 2024

Understanding jenkinsfile, Dockerfile, and deployment YAML files

JenkinsFile is loaded and called by jenkins server for build and deployment. Ths code in this file will run in the default jnlp container.

The podTemplate method defined in Jenkins file specifies multiple container's with name and image id. Inside the podTemplate method, it can access git commit and branch info through scmInof.GIT_COMMIT and scmInof.GIT_BRANCH.

You can define multiple steps for build and deployment by calling stage method with their unique name, if a stage succeeds, it will run the next one. If a stage fails, it will abort the jenkins file execution. Within each stage method implementation, call container () method with name parameter to indicate which container should run the code, 

In one step, the stage method is called on nodejs container to build the project

In one following step, the stage method is called on docker container to build the docker image, which will use the Dockerfile, it then tags docker image, and push it to docker registration.

After the docker image is published, then a new stage method can run in kubectl container to apply the infrastructure yaml file defined for each deployment, within the deployment yaml file, it specifies the docker image id which is published in previous step, and create a new container image for its service.


Please refer to the below link for more details

https://www.jenkins.io/doc/pipeline/steps/kubernetes/


 



Saturday, June 17, 2023

Angular key points for interview and review

1. app component and app module

Angular application starts from app component, which is created by "ng new" command, the app component is loaded from index.html. 

app.module.ts defines all components implement and export from the project as well as components imported by the project.


2. component

Component is defined by ts file, template html file, and optional css file. Ts file is the key file, which defines the component element name, as well as template html file and css file name.


3. string interpolation

In component html template file, if any property is defined by ts file, then inside html template file, the content of the property can be embedded using string interpolation syntax {{propertyname}}


4. property binding

In component html template file, to set an attribute for an html element using property binding with the below syntax <img alt="item" [src]="itemImageUrl">, note itemImageUrl is the property exposed from ts file. src is the element attribute name, which needs a square bracket.


5. event binding

For input element, once a method is defined in ts file, then in html template file, the event can be bind to the method using below syntax:

<button (click)="onSave($event)">Save</button>

click is the event name for the element without "on", and onSave is the method defined in ts file. $event is the event parameter.


6. two way binding using [(ngModel)]

In order to make two way binding work for html input elements, first in app.module.ts import FormsModule as below

import { FormsModule } from '@angular/forms'; 

Then using the below syntax to set two way binding

<input type="text" [(ngModel)]="somepropertydefinedinTSfile" />

As a result, the value input by user will be set to somepropertydefinedinTSfile, and value set to somepropertydefinedinTSfile will show in the input element.


7. *ngIf for conditional rendering

For any html element, *ngIf can be used to conditionally render the element, if the value of *ngIf is true, then rendering the element. Otherwise, not rendering the element.

<p *ngIf="shouldRenderThisElement">my name is {{myName}}</p>


8. use ngStyle and ngClass to set css style dynamically

In order to dynamically set a css style for an element, use the below ngStyle syntax

<p [ngStyle]="{backgroundColor: getColor()}">my text</p>

getColor() is the method implemented in ts file to return a string of color name, like "red".

In order to dynamically set a css class for an element, use the below ngClass syntax

<p [ngClass]="{onlineCssClass: applyOnlineCssClass === true}"> my text </p>

where onlineCssClass is a css class defined in css file, and applyOnlineCssClass is a boolean property defined and exported from ts file.


9. use *ngFor to render array object

ngFor will loop through an array and render each array element into a separate html element as shown below:

<serverComponent *ngFor="let server of myServerArray; let i = index">{{i}}: {{server.name}}</serverComponent>


10. using @Input and @Output for passing data between parent and child component

When parent component initializes children components, parent can pass data to children by setting the children component's attributes. The children component's attributes must be exposed to external using @Input directive. The input direct can also take a parameter as alias of the inner property.

@Input() myColor = 'red'

The child component can use @Outeput directive to raise an event to notify the parent, @Output must have the type of EventEmitter.

Child TS file:

@Output() newItemEvent = new EventEmitter<string>();

addNewItem(value: string) {

this.newItemEvent.emit(value); }

Child Html file:

<button type="button" (click)="addNewItem(newItem.value)">Add to parent</button>

Parent TS file

addItem(newItem: string) { this.items.push(newItem); }

Parent Html file:

<app-item-output (newItemEvent)="addItem($event)"></app-item-output>


11. ViewEncapsulation setting

Usually the css style only applies to the component elements, however by setting the component directive ViewEncapsulation to none, the defined css styles will become global and applies to all matched html elements.

encapsulation: ViewEncapsulation.None


12 html template local reference

In component html template file, for any html element, add #localReferenceName as attribute will set the html element to this local reference variable. In the template file, the local reference variable can be used anywhere as the html element's value. A particular use case is passing the local reference variable as event callback parameter, so TS file can get access to the html DOM element.

13 using @ViewChild to access local reference from ts file

After defining a local template reference, without passing it as event callback method parameter, ts file can also use @viewChild to access the local reference as ElemetnRef type as below code in ts file

@ViewChild('mylocalTemplateVariableName', {static: true}) myinputElement: ElementRef;

ElementRef has a property of value to get access the html DOM element.


14. life cycle hook

ngOnChanges: called after a bound input property changes

ngOnInit: called ocne hte component is initialized

ngAfterViewInit: called after the component's view has been initialized

ngOnDestry: called after the component is destroyed


15. Service provider

First implement a regular javascript class as service, and add @Injectabel({providedIn: 'root'}) decorator in the class

Then in the component uses the service, in constructor, add a private parameter for the service type, angular will inject a shared service instance to be used by the component. One special use case for shared service is using service for cross component communication.


16. Router

In app.module.ts, define router pattern, and add the router into imports section

RouterModule,forRoot(myappRouterConfig)

Then in app component html file or any sub component html file, add the below code, which will render the matched component in it

<router-outlet></router-outlet>

In the html a href link element that load the specific routing path, use routeLink to set the target url, so it will skip reloading the page from server as a regular link does

<a routeLink="/myLinkUrl" routerLinkActive="myActiveCssClass">my link</a>


You can also programmatically router to a specific url using Router package, 

import {Router} from '@angular/router'

constructor(private router: Router) {}

onMyClickEvent() {

this.router.navigate(['/myservers']);


17. Using ActiveRoute to get router url parameter

If the router path includes dynamic parameter preceding with ':', such as "users/:id/:name", then ActiveRoute service can be used to retrieve the router url parameters from "/users/1/john' as below:

userId = this.route.snapshot.params['id'];  //1

userName = this.route.snapshot.params['name'];  //john

Note route.snapshot only take the value when the component is created. After the creation, if the url parameter is changed, the snapshot.params value will not change. So if you need to get the updated url parameter, you will need to subscribe to this.route.params as below in ngOnInit() method

this.route.params.subscribe( (params: Params) = {

    userId = params['id'];

    userName = params['name'];

}


18 Protect router path using CanActivate 

In order to prevent unauthorized user to access a router path, you can implement a service which implements CanActivate interface and then set the router path canActivate attribute to this service. When the router path is loaded, the canActivate method will be called, and if the method return false, it will prevent the load process. You can also redirect to login page if canActivate return false.


19. Observable, pipe and Subject

By using rsjx library, you can subscribe to an Observable and implement next, error, complete callback method to handle the date, error or complete event.

Pipe is an operations that you can chain to process the data provided data by observer, the processed data will be passed to the subscriber.

Subject exposes both the next() method to feed the data, and the observable that allows subscribers to subscribe, so you do not need to implement the raw observable.


20. Form submission with FormModule 

First import FormModule in app.module.ts, then in html template file with form element, for each input element that should be included in the form data, add ngModel in its attribute, and also set the name attribute

<input type="text" id="username" ngModel name="username"/>

the above ngModel attribute also be changed to property binding [ngModel] or two way binding [(ngModel)].

in form element, set the form submit method as below

<form (ngSubmit)="myFormsubmitMethod(f)" #f="ngForm">

Then when submit button is clicked, parameter f as type of NgForm will contain the form data.

As another option to get the ngForm data from submit method'a parameter, @ViewChild can also be used as below in TS file

@ViewChild('f') myForm: NgForm;

onSubmit(){

    console.log(this.myForm);

}

If you need to handle individual input element in html template file, you can also set a local reference variable to it as type of ngModel

<input type="text" id="username" ngModel name="username" #myname = "ngModel" />


21 Form submission with React form

React form lets ts code to directly access the FormGroup and FromControl elements. 

First import FormGroup and FormControl into app.module.ts. Then in ngOnInit() method, create the FormGroup object that contains few FormControl element as below

myForm: FormGroup;

ngOnInit() {

    this.myForm = new FormGroup({

        'username' : new FormControl(null, Validators.required),

        'email': new FormControl(null, [Validators.required, Validators.email])

    })

}

onSubmit() {

    console.log(this.myForm);

}

In html template code, update as below 

<form [formgroup]='myForm' (ngSubmit)="onSubmit()">

      <input type="text" id = "username" formControlName="username">

      <input type="text" id="email" formControlName="email">

</form>

In addition of FormGroup and FormControl, FormArray can be used to handle array input element.


22 HttpClient for sending server request

HttpClient class can be used to send server request by calling post or get method, it returns an observable, so you can subscribe a callback method to handle the server response.

It is common practice to call pipe(operatorCallback) on HttpClient.get or post method, so you can transform the data before subscribe on it.

Tuesday, August 30, 2022

Azure AD B2C configuration note

 In Azure AD B2C, multiple user flows can be defined for different purposes, for example, signin, signup, or reset password. These user flows can be used by any app registration, so they are global AD B2C configuration, and do not belong to a particular app registration.

In order to test Azure AD B2C with a particular web app, theoretically, there is nothing that need to be changed in application side, an web or SPA app can just hook up with Azure AD B2C without any changes. In the end, after Azure authentication is finished, a bearer token is provided by Azure AD B2C in request header to the web app,  and web app can validate the token to accept or reject the request, but if the web app does nothing, then it should work as usual.

When registering an app to Azure AD B2C, it needs to provide a redirect URL to tell Azure B2C where to redirect the client after Azure B2C authentication. The app registration also generate an application ID (or client id), which the client request must provide when it initializes authentication request, so Azure B2C can validate the request is sent from a registered application.  

Sunday, July 24, 2022

When to use useReducer over useState in react

Both useState and useReducer are react hooks to handle state management. The useState is the default choice, and useReducer should only be used when state management logic is complex.

But why useReducer is better than useState when managing complex state in react project?

For useState hook, developers call setState method returned by useState()  to update the state, setState accepts a callback method implemented by app developer, which has a single parameter of the previous state object, developer can return a new updated state based on the current state and application logic.

const [state, setState] = useState({});
setState(prevState => {
  // Object.assign would also work
  return {...prevState, ...updatedValues};
});

Assuming 5 developers work on 5 different modules of the same project, and each developer may call setState to update the shared state object of the project. The problem is some state values set by one developer may break the state set by other developers. As there is not a a center place to manage the state, and each developer can set the state based on his own logic without knowing the effect on other developer's logic.

To solve this problem, the manager of the 5 developers decide to useReducer to solve this issue, 

const [state, dispatch] = useReducer(reducer, initialState)

The manager implements the reducer method as the single method to update the state, so people can audit the state object's update history if inconsistence happens again. Each developer cannot no longer directly update the state object. In stead, if developer need to update the state, he must call the dispatch method with related parameter, the dispatch method will trigger the call of reducer method with the parameter provided by developers. The reducer method will actually update the state and also maintain the consistence of the state object.

So the key difference between useState and useReducer is useReducer provides another layer of separation, and it isolates the role of the state change requester and state change handler. Anyone can send a state change request by calling the dispatch method, but only the state change handler (the implementation of reducer method) can decide how the request is handled so as to be sure the state object is always valid.


Monday, June 27, 2022

Break for javascript debug in chrome when sending xhr/fetch request

It is helpful to check the server request and response in chrome for javascript code, particularly if you do not have the source code information available to set breakpoint in a particular lines.

For chrome, this is easy to do by opening the developer console, and then open source tab, on the right side panel, under the section title of "XHR/fetch Breakpoints", add a new break point item for "Any XHR or fetch". 

The breakpoints requests to input string to break on matched url. In order to break on any xhr or fetch requests, just set the input string as '.', note '*' does not work for matching all purpose.


Thursday, June 23, 2022

Closure for javascript

In MDN web doc , A closure in JavaScript is defined as:

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

The definition is not very clear as the closure involves both parent function and inner functions. So it needs more explanation about the definition:


When a (parent) function is executed, the function body will have all the local variables set to some value for the function execution. This execution environment including all the local variables is called parent function's lexical environment. 

If any inner function is defined within the parent function body, then the parent function's lexical environment will provide the variables defined in parent function, but referred by the inner function. Those variables are called closure variables.

The closure is created when the parent function is executed, and for each execution of parent function, a new closure environment is created. This makes sense, as each execution will have different variable values inside the parent function. All the inner functions defined in the parent function body, and whenever they are called, they will share the same lexical environment, i.e. the local variable's latest values declared in the parent function. 

So a closure is created when every time the parent function is executed, it captures all the local variables used by that execution instance. 

Note the closure only keeps the latest values of all the local variables defined in the parent function, so it the parent function does a for loop to update a local variable, then the local variable will have the latest value after the for loop finished. If a inner function is called later, then the inner function can only see the latest value of the parent function's closure variables, certainly the inner function can also update the closure variable to be visible to other inner functions.